field_test 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b7676acb9075e7701de0019e3c7ca18a0242db8c87348d024006ff787c42d13
4
- data.tar.gz: 92849469d6ca65422ef3f2a1b77f08c7695d79fa336b463cedf3991770be15cd
3
+ metadata.gz: 5dfb996b9bbcbdec8cc8b590f319469981b9703e08b7940e1e2929e109849286
4
+ data.tar.gz: 2a9029f33c3a825810c664ffe7eb9faf8275c7b2f915cc8f6c4c7f698da7e2ae
5
5
  SHA512:
6
- metadata.gz: 9f21fa35a6f44d085bbbfbd15e8174e7dd7f8a0d411e3a584097d59793776a0c0bd783cb4a3d7fee7f07402190a1a61487e1be554492208536f375b3e1981c7f
7
- data.tar.gz: aeb943baab224022931a634fa190bbb3837856b3f0ee956c919f1233c6c6796a5251216a65334cd34878487e5902a251e695722a8982a19ffca6711e1d6113cf
6
+ metadata.gz: cfe04c537b3b1b67b058a0b257d5f0abe6a26420eb227b9ccc41b068a41f5d21f585c127e5f712a5c5e79894cc4dae69de136bd929c1b38089ed93d7d7eae115
7
+ data.tar.gz: ec40759e91c68791f4a39423332024964806a4fde398d9290b2b8a904b14ecdb4fce4d9585af8cf1c40d2a413611f53ca8f4841c0445cf34b6e1be49f4e7efb2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.5.2 (2021-12-16)
2
+
3
+ - Fixed error with `events` association
4
+ - Fixed `exclude` option with custom logic
5
+ - Fixed installation error on macOS 12
6
+
1
7
  ## 0.5.1 (2021-09-22)
2
8
 
3
9
  - Improved performance of Bayesian calculations
data/README.md CHANGED
@@ -34,8 +34,6 @@ mount FieldTest::Engine, at: "field_test"
34
34
 
35
35
  Be sure to [secure the dashboard](#dashboard-security) in production.
36
36
 
37
- ![Screenshot](https://ankane.github.io/field_test/screenshot6.png)
38
-
39
37
  ## Getting Started
40
38
 
41
39
  Add an experiment to `config/field_test.yml`.
@@ -427,5 +425,6 @@ To get started with development:
427
425
  git clone https://github.com/ankane/field_test.git
428
426
  cd field_test
429
427
  bundle install
428
+ bundle exec rake compile
430
429
  bundle exec rake test
431
430
  ```
@@ -2,7 +2,7 @@ module FieldTest
2
2
  class Membership < ActiveRecord::Base
3
3
  self.table_name = "field_test_memberships"
4
4
 
5
- has_many :events, class_name: "FieldTest::Event"
5
+ has_many :events, class_name: "FieldTest::Event", foreign_key: "field_test_membership_id"
6
6
 
7
7
  validates :participant, presence: true, if: -> { FieldTest.legacy_participants }
8
8
  validates :participant_id, presence: true, if: -> { !FieldTest.legacy_participants }
@@ -0,0 +1,313 @@
1
+ /*!
2
+ * BayesTest C++ v0.1.0
3
+ * https://github.com/ankane/bayestest-cpp
4
+ * MIT License
5
+ */
6
+
7
+ #pragma once
8
+
9
+ #include <cmath>
10
+ #include <vector>
11
+
12
+ namespace bayestest {
13
+
14
+ double logbeta(double a, double b) {
15
+ return std::lgamma(a) + std::lgamma(b) - std::lgamma(a + b);
16
+ }
17
+
18
+ double prob_b_beats_a(int alpha_a, int beta_a, int alpha_b, int beta_b) {
19
+ double total = 0.0;
20
+ double logbeta_aa_ba = logbeta(alpha_a, beta_a);
21
+ double beta_ba = beta_b + beta_a;
22
+
23
+ for (auto i = 0; i < alpha_b; i++) {
24
+ total += exp(logbeta(alpha_a + i, beta_ba) - log(beta_b + i) - logbeta(1 + i, beta_b) - logbeta_aa_ba);
25
+ }
26
+
27
+ return total;
28
+ }
29
+
30
+ double prob_c_beats_ab(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c) {
31
+ double total = 0.0;
32
+
33
+ double logbeta_ac_bc = logbeta(alpha_c, beta_c);
34
+
35
+ std::vector<double> log_bb_j_logbeta_j_bb;
36
+ log_bb_j_logbeta_j_bb.reserve(alpha_b);
37
+
38
+ for (auto j = 0; j < alpha_b; j++) {
39
+ log_bb_j_logbeta_j_bb.push_back(log(beta_b + j) + logbeta(1 + j, beta_b));
40
+ }
41
+
42
+ double abc = beta_a + beta_b + beta_c;
43
+ std::vector<double> logbeta_ac_i_j;
44
+ logbeta_ac_i_j.reserve(alpha_a + alpha_b);
45
+
46
+ for (auto i = 0; i < alpha_a + alpha_b; i++) {
47
+ logbeta_ac_i_j.push_back(logbeta(alpha_c + i, abc));
48
+ }
49
+
50
+ for (auto i = 0; i < alpha_a; i++) {
51
+ double sum_i = -log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ac_bc;
52
+
53
+ for (auto j = 0; j < alpha_b; j++) {
54
+ total += exp(sum_i + logbeta_ac_i_j[i + j] - log_bb_j_logbeta_j_bb[j]);
55
+ }
56
+ }
57
+
58
+ return 1 - prob_b_beats_a(alpha_c, beta_c, alpha_a, beta_a) -
59
+ prob_b_beats_a(alpha_c, beta_c, alpha_b, beta_b) + total;
60
+ }
61
+
62
+ double prob_d_beats_abc(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c, int alpha_d, int beta_d) {
63
+ double total = 0.0;
64
+
65
+ double logbeta_ad_bd = logbeta(alpha_d, beta_d);
66
+
67
+ std::vector<double> log_bb_j_logbeta_j_bb;
68
+ log_bb_j_logbeta_j_bb.reserve(alpha_b);
69
+
70
+ for (auto j = 0; j < alpha_b; j++) {
71
+ log_bb_j_logbeta_j_bb.push_back(log(beta_b + j) + logbeta(1 + j, beta_b));
72
+ }
73
+
74
+ std::vector<double> log_bc_k_logbeta_k_bc;
75
+ log_bc_k_logbeta_k_bc.reserve(alpha_c);
76
+
77
+ for (auto k = 0; k < alpha_c; k++) {
78
+ log_bc_k_logbeta_k_bc.push_back(log(beta_c + k) + logbeta(1 + k, beta_c));
79
+ }
80
+
81
+ double abcd = beta_a + beta_b + beta_c + beta_d;
82
+ std::vector<double> logbeta_bd_i_j_k;
83
+ logbeta_bd_i_j_k.reserve(alpha_a + alpha_b + alpha_c);
84
+
85
+ for (auto i = 0; i < alpha_a + alpha_b + alpha_c; i++) {
86
+ logbeta_bd_i_j_k.push_back(logbeta(alpha_d + i, abcd));
87
+ }
88
+
89
+ for (auto i = 0; i < alpha_a; i++) {
90
+ double sum_i = -log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ad_bd;
91
+
92
+ for (auto j = 0; j < alpha_b; j++) {
93
+ double sum_j = sum_i - log_bb_j_logbeta_j_bb[j];
94
+
95
+ for (auto k = 0; k < alpha_c; k++) {
96
+ total += exp(sum_j + logbeta_bd_i_j_k[i + j + k] - log_bc_k_logbeta_k_bc[k]);
97
+ }
98
+ }
99
+ }
100
+
101
+ return 1 - prob_b_beats_a(alpha_a, beta_a, alpha_d, beta_d) -
102
+ prob_b_beats_a(alpha_b, beta_b, alpha_d, beta_d) -
103
+ prob_b_beats_a(alpha_c, beta_c, alpha_d, beta_d) +
104
+ prob_c_beats_ab(alpha_a, beta_a, alpha_b, beta_b, alpha_d, beta_d) +
105
+ prob_c_beats_ab(alpha_a, beta_a, alpha_c, beta_c, alpha_d, beta_d) +
106
+ prob_c_beats_ab(alpha_b, beta_b, alpha_c, beta_c, alpha_d, beta_d) - total;
107
+ }
108
+
109
+ double prob_1_beats_2(int alpha_1, int beta_1, int alpha_2, int beta_2) {
110
+ double total = 0.0;
111
+ double log_b1 = log(beta_1);
112
+ double a2_log_b2 = alpha_2 * log(beta_2);
113
+ double log_b1_b2 = log(beta_1 + beta_2);
114
+
115
+ for (auto k = 0; k < alpha_1; k++) {
116
+ total += exp(k * log_b1 +
117
+ a2_log_b2 -
118
+ (k + alpha_2) * log_b1_b2 -
119
+ log(k + alpha_2) -
120
+ logbeta(k + 1, alpha_2));
121
+ }
122
+
123
+ return total;
124
+ }
125
+
126
+ double prob_1_beats_23(int alpha_1, int beta_1, int alpha_2, int beta_2, int alpha_3, int beta_3) {
127
+ double total = 0.0;
128
+
129
+ double log_b1_b2_b3 = log(beta_1 + beta_2 + beta_3);
130
+ double a1_log_b1 = alpha_1 * log(beta_1);
131
+ double log_b2 = log(beta_2);
132
+ double log_b3 = log(beta_3);
133
+ double loggamma_a1 = std::lgamma(alpha_1);
134
+
135
+ for (auto k = 0; k < alpha_2; k++) {
136
+ double sum_k = a1_log_b1 + k * log_b2 - std::lgamma(k + 1);
137
+
138
+ for (auto l = 0; l < alpha_3; l++) {
139
+ total += exp(sum_k + l * log_b3
140
+ - (k + l + alpha_1) * log_b1_b2_b3
141
+ + std::lgamma(k + l + alpha_1) - std::lgamma(l + 1) - loggamma_a1);
142
+ }
143
+ }
144
+
145
+ return 1.0 - prob_1_beats_2(alpha_2, beta_2, alpha_1, beta_1)
146
+ - prob_1_beats_2(alpha_3, beta_3, alpha_1, beta_1) + total;
147
+ }
148
+
149
+ class BinaryTest {
150
+ public:
151
+ void add(int participants, int conversions) {
152
+ variants.emplace_back(participants, conversions);
153
+ }
154
+
155
+ std::vector<double> probabilities() {
156
+ std::vector<double> probs;
157
+ probs.reserve(variants.size());
158
+
159
+ switch (variants.size()) {
160
+ case 0: {
161
+ break;
162
+ }
163
+ case 1: {
164
+ probs.push_back(1);
165
+
166
+ break;
167
+ }
168
+ case 2: {
169
+ auto b = variants[0];
170
+ auto a = variants[1];
171
+
172
+ auto prob = prob_b_beats_a(
173
+ 1 + a.conversions,
174
+ 1 + a.participants - a.conversions,
175
+ 1 + b.conversions,
176
+ 1 + b.participants - b.conversions
177
+ );
178
+ probs.push_back(prob);
179
+ probs.push_back(1 - prob);
180
+
181
+ break;
182
+ }
183
+ case 3: {
184
+ auto total = 0.0;
185
+ for (auto i = 0; i < 2; i++) {
186
+ auto c = variants[i];
187
+ auto b = variants[(i + 1) % 3];
188
+ auto a = variants[(i + 2) % 3];
189
+
190
+ auto prob = prob_c_beats_ab(
191
+ 1 + a.conversions,
192
+ 1 + a.participants - a.conversions,
193
+ 1 + b.conversions,
194
+ 1 + b.participants - b.conversions,
195
+ 1 + c.conversions,
196
+ 1 + c.participants - c.conversions
197
+ );
198
+
199
+ probs.push_back(prob);
200
+ total += prob;
201
+ }
202
+ probs.push_back(1 - total);
203
+
204
+ break;
205
+ }
206
+ default: {
207
+ auto total = 0.0;
208
+ for (auto i = 0; i < 3; i++) {
209
+ auto d = variants[i];
210
+ auto c = variants[(i + 1) % 4];
211
+ auto b = variants[(i + 2) % 4];
212
+ auto a = variants[(i + 3) % 4];
213
+
214
+ auto prob = prob_d_beats_abc(
215
+ 1 + a.conversions,
216
+ 1 + a.participants - a.conversions,
217
+ 1 + b.conversions,
218
+ 1 + b.participants - b.conversions,
219
+ 1 + c.conversions,
220
+ 1 + c.participants - c.conversions,
221
+ 1 + d.conversions,
222
+ 1 + d.participants - d.conversions
223
+ );
224
+
225
+ probs.push_back(prob);
226
+ total += prob;
227
+ }
228
+ probs.push_back(1 - total);
229
+ }
230
+ }
231
+ return probs;
232
+ }
233
+
234
+ private:
235
+ struct Variant {
236
+ Variant(int participants, int conversions) : participants(participants), conversions(conversions) {};
237
+ int participants;
238
+ int conversions;
239
+ };
240
+
241
+ std::vector<Variant> variants;
242
+ };
243
+
244
+ class CountTest {
245
+ public:
246
+ void add(int events, int exposure) {
247
+ variants.emplace_back(events, exposure);
248
+ }
249
+
250
+ std::vector<double> probabilities() {
251
+ std::vector<double> probs;
252
+ probs.reserve(variants.size());
253
+
254
+ switch (variants.size()) {
255
+ case 0: {
256
+ break;
257
+ }
258
+ case 1: {
259
+ probs.push_back(1);
260
+
261
+ break;
262
+ }
263
+ case 2: {
264
+ auto a = variants[0];
265
+ auto b = variants[1];
266
+
267
+ auto prob = prob_1_beats_2(
268
+ a.events,
269
+ a.exposure,
270
+ b.events,
271
+ b.exposure
272
+ );
273
+ probs.push_back(prob);
274
+ probs.push_back(1 - prob);
275
+
276
+ break;
277
+ }
278
+ default: {
279
+ auto total = 0.0;
280
+ for (auto i = 0; i < 2; i++) {
281
+ auto a = variants[i];
282
+ auto b = variants[(i + 1) % 3];
283
+ auto c = variants[(i + 2) % 3];
284
+
285
+ auto prob = prob_1_beats_23(
286
+ a.events,
287
+ a.exposure,
288
+ b.events,
289
+ b.exposure,
290
+ c.events,
291
+ c.exposure
292
+ );
293
+
294
+ probs.push_back(prob);
295
+ total += prob;
296
+ }
297
+ probs.push_back(1 - total);
298
+ }
299
+ }
300
+ return probs;
301
+ }
302
+
303
+ private:
304
+ struct Variant {
305
+ Variant(int events, int exposure) : events(events), exposure(exposure) {};
306
+ int events;
307
+ int exposure;
308
+ };
309
+
310
+ std::vector<Variant> variants;
311
+ };
312
+
313
+ }
@@ -1,12 +1,16 @@
1
1
  #include <rice/rice.hpp>
2
- #include "bayesian_ab.hpp"
2
+ #include <rice/stl.hpp>
3
+
4
+ #include "bayestest.hpp"
5
+
6
+ using bayestest::BinaryTest;
3
7
 
4
8
  extern "C"
5
9
  void Init_ext() {
6
10
  auto rb_mFieldTest = Rice::define_module("FieldTest");
7
11
 
8
- Rice::define_class_under(rb_mFieldTest, "Calculations")
9
- .define_singleton_function("prob_b_beats_a", &bayesian_ab::prob_b_beats_a)
10
- .define_singleton_function("prob_c_beats_a_and_b", &bayesian_ab::prob_c_beats_a_and_b)
11
- .define_singleton_function("prob_d_beats_a_and_b_and_c", &bayesian_ab::prob_d_beats_a_and_b_and_c);
12
+ Rice::define_class_under<BinaryTest>(rb_mFieldTest, "BinaryTest")
13
+ .define_constructor(Rice::Constructor<BinaryTest>())
14
+ .define_method("add", &BinaryTest::add)
15
+ .define_method("probabilities", &BinaryTest::probabilities);
12
16
  }
@@ -141,40 +141,21 @@ module FieldTest
141
141
  }
142
142
  end
143
143
 
144
- case variants.size
145
- when 1, 2, 3
146
- total = 0.0
147
-
148
- (variants.size - 1).times do |i|
149
- c = results.values[i]
150
- b = results.values[(i + 1) % variants.size]
151
- a = results.values[(i + 2) % variants.size]
152
-
153
- alpha_a = 1 + a[:converted]
154
- beta_a = 1 + a[:participated] - a[:converted]
155
- alpha_b = 1 + b[:converted]
156
- beta_b = 1 + b[:participated] - b[:converted]
157
- alpha_c = 1 + c[:converted]
158
- beta_c = 1 + c[:participated] - c[:converted]
159
-
160
- # TODO calculate this incrementally by caching intermediate results
161
- prob_winning =
162
- if variants.size == 2
163
- cache_fetch ["field_test", "prob_b_beats_a", alpha_b, beta_b, alpha_c, beta_c] do
164
- Calculations.prob_b_beats_a(alpha_b, beta_b, alpha_c, beta_c)
165
- end
166
- else
167
- cache_fetch ["field_test", "prob_c_beats_a_and_b", alpha_a, beta_a, alpha_b, beta_b, alpha_c, beta_c] do
168
- Calculations.prob_c_beats_a_and_b(alpha_a, beta_a, alpha_b, beta_b, alpha_c, beta_c)
169
- end
144
+ if variants.size <= 3
145
+ probabilities =
146
+ cache_fetch(["field_test", "probabilities"] + results.flat_map { |_, v| [v[:participated], v[:converted]] }) do
147
+ binary_test = BinaryTest.new
148
+ results.each do |_, v|
149
+ binary_test.add(v[:participated], v[:converted])
170
150
  end
151
+ binary_test.probabilities.to_a
152
+ end
171
153
 
172
- results[variants[i]][:prob_winning] = prob_winning
173
- total += prob_winning
154
+ results.each_key.zip(probabilities) do |variant, prob_winning|
155
+ results[variant][:prob_winning] = prob_winning
174
156
  end
175
-
176
- results[variants.last][:prob_winning] = 1 - total
177
157
  end
158
+
178
159
  results
179
160
  end
180
161
 
@@ -13,7 +13,7 @@ module FieldTest
13
13
  end
14
14
 
15
15
  if FieldTest.exclude_bots?
16
- options[:exclude] = Browser.new(request.user_agent).bot?
16
+ options[:exclude] ||= Browser.new(request.user_agent).bot?
17
17
  end
18
18
 
19
19
  options[:exclude] ||= FieldTest.excluded_ips.any? { |ip| ip.include?(request.remote_ip) }
@@ -1,3 +1,3 @@
1
1
  module FieldTest
2
- VERSION = "0.5.1"
2
+ VERSION = "0.5.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: field_test
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-23 00:00:00.000000000 Z
11
+ date: 2021-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -89,7 +89,7 @@ files:
89
89
  - app/views/field_test/participants/show.html.erb
90
90
  - app/views/layouts/field_test/application.html.erb
91
91
  - config/routes.rb
92
- - ext/field_test/bayesian_ab.hpp
92
+ - ext/field_test/bayestest.hpp
93
93
  - ext/field_test/ext.cpp
94
94
  - ext/field_test/extconf.rb
95
95
  - lib/field_test.rb
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  requirements: []
127
- rubygems_version: 3.2.22
127
+ rubygems_version: 3.2.32
128
128
  signing_key:
129
129
  specification_version: 4
130
130
  summary: A/B testing for Rails
@@ -1,103 +0,0 @@
1
- #pragma once
2
-
3
- #include <cmath>
4
- #include <vector>
5
-
6
- namespace bayesian_ab {
7
-
8
- double logbeta(double a, double b) {
9
- return std::lgamma(a) + std::lgamma(b) - std::lgamma(a + b);
10
- }
11
-
12
- double prob_b_beats_a(int alpha_a, int beta_a, int alpha_b, int beta_b) {
13
- double total = 0.0;
14
- double logbeta_aa_ba = logbeta(alpha_a, beta_a);
15
- double beta_ba = beta_b + beta_a;
16
-
17
- for (auto i = 0; i < alpha_b; i++) {
18
- total += exp(logbeta(alpha_a + i, beta_ba) - log(beta_b + i) - logbeta(1 + i, beta_b) - logbeta_aa_ba);
19
- }
20
-
21
- return total;
22
- }
23
-
24
- double prob_c_beats_a_and_b(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c) {
25
- double total = 0.0;
26
-
27
- double logbeta_ac_bc = logbeta(alpha_c, beta_c);
28
-
29
- std::vector<double> log_bb_j_logbeta_j_bb;
30
- log_bb_j_logbeta_j_bb.reserve(alpha_b);
31
-
32
- for (auto j = 0; j < alpha_b; j++) {
33
- log_bb_j_logbeta_j_bb.push_back(log(beta_b + j) + logbeta(1 + j, beta_b));
34
- }
35
-
36
- double abc = beta_a + beta_b + beta_c;
37
- std::vector<double> logbeta_ac_i_j;
38
- logbeta_ac_i_j.reserve(alpha_a + alpha_b);
39
-
40
- for (auto i = 0; i < alpha_a + alpha_b; i++) {
41
- logbeta_ac_i_j.push_back(logbeta(alpha_c + i, abc));
42
- }
43
-
44
- for (auto i = 0; i < alpha_a; i++) {
45
- double sum_i = -log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ac_bc;
46
-
47
- for (auto j = 0; j < alpha_b; j++) {
48
- total += exp(sum_i + logbeta_ac_i_j[i + j] - log_bb_j_logbeta_j_bb[j]);
49
- }
50
- }
51
-
52
- return 1 - prob_b_beats_a(alpha_c, beta_c, alpha_a, beta_a) -
53
- prob_b_beats_a(alpha_c, beta_c, alpha_b, beta_b) + total;
54
- }
55
-
56
- double prob_d_beats_a_and_b_and_c(int alpha_a, int beta_a, int alpha_b, int beta_b, int alpha_c, int beta_c, int alpha_d, int beta_d) {
57
- double total = 0.0;
58
-
59
- double logbeta_ad_bd = logbeta(alpha_d, beta_d);
60
-
61
- std::vector<double> log_bb_j_logbeta_j_bb;
62
- log_bb_j_logbeta_j_bb.reserve(alpha_b);
63
-
64
- for (auto j = 0; j < alpha_b; j++) {
65
- log_bb_j_logbeta_j_bb.push_back(log(beta_b + j) + logbeta(1 + j, beta_b));
66
- }
67
-
68
- std::vector<double> log_bc_k_logbeta_k_bc;
69
- log_bc_k_logbeta_k_bc.reserve(alpha_c);
70
-
71
- for (auto k = 0; k < alpha_c; k++) {
72
- log_bc_k_logbeta_k_bc.push_back(log(beta_c + k) + logbeta(1 + k, beta_c));
73
- }
74
-
75
- double abcd = beta_a + beta_b + beta_c + beta_d;
76
- std::vector<double> logbeta_bd_i_j_k;
77
- logbeta_bd_i_j_k.reserve(alpha_a + alpha_b + alpha_c);
78
-
79
- for (auto i = 0; i < alpha_a + alpha_b + alpha_c; i++) {
80
- logbeta_bd_i_j_k.push_back(logbeta(alpha_d + i, abcd));
81
- }
82
-
83
- for (auto i = 0; i < alpha_a; i++) {
84
- double sum_i = -log(beta_a + i) - logbeta(1 + i, beta_a) - logbeta_ad_bd;
85
-
86
- for (auto j = 0; j < alpha_b; j++) {
87
- double sum_j = sum_i - log_bb_j_logbeta_j_bb[j];
88
-
89
- for (auto k = 0; k < alpha_c; k++) {
90
- total += exp(sum_j + logbeta_bd_i_j_k[i + j + k] - log_bc_k_logbeta_k_bc[k]);
91
- }
92
- }
93
- }
94
-
95
- return 1 - prob_b_beats_a(alpha_a, beta_a, alpha_d, beta_d) -
96
- prob_b_beats_a(alpha_b, beta_b, alpha_d, beta_d) -
97
- prob_b_beats_a(alpha_c, beta_c, alpha_d, beta_d) +
98
- prob_c_beats_a_and_b(alpha_a, beta_a, alpha_b, beta_b, alpha_d, beta_d) +
99
- prob_c_beats_a_and_b(alpha_a, beta_a, alpha_c, beta_c, alpha_d, beta_d) +
100
- prob_c_beats_a_and_b(alpha_b, beta_b, alpha_c, beta_c, alpha_d, beta_d) - total;
101
- }
102
-
103
- }