浏览代码

[ML] add new normalize_above parameter to p_value significant terms heuristic (#78833)

This commit adds the new normalize_above parameter to the p_value significant
terms heuristic.

This parameter allows for consistent significance results at various scales. When a total count (in or out of the set background set) is above the normalize_above parameter, both the total set and the set including the term are scaled by normalize_above/count where count is term in the set or total set size.
Benjamin Trent 4 年之前
父节点
当前提交
843fa42c1e

+ 5 - 1
docs/reference/aggregations/bucket/significantterms-aggregation.asciidoc

@@ -404,6 +404,10 @@ the foreground set of "ended in failure" versus "NOT ended in failure".
 `"background_is_superset": false` indicates that the background set does 
 `"background_is_superset": false` indicates that the background set does 
 not contain the counts of the foreground set as they are filtered out.
 not contain the counts of the foreground set as they are filtered out.
 
 
+`"normalize_above": 1000` facilitates returning consistent significance results
+at various scales. `1000` indicates that term counts greater than `1000` are
+scaled down by a factor of `1000/term_count`.
+
 [source,console]
 [source,console]
 --------------------------------------------------
 --------------------------------------------------
 GET /_search
 GET /_search
@@ -466,7 +470,7 @@ GET /_search
             ]
             ]
           }
           }
         },
         },
-        "p_value": {"background_is_superset": false}
+        "p_value": {"background_is_superset": false, "normalize_above": 1000}
       }
       }
     }
     }
   }
   }

+ 6 - 2
test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractSignificanceHeuristicTestCase.java

@@ -37,6 +37,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.SignificantTermsAggreg
 import org.elasticsearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic;
 import org.elasticsearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.InternalAggregationTestCase;
 import org.elasticsearch.test.InternalAggregationTestCase;
+import org.elasticsearch.test.VersionUtils;
 
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ByteArrayOutputStream;
@@ -52,7 +53,6 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonList;
 import static org.elasticsearch.search.aggregations.AggregationBuilders.significantTerms;
 import static org.elasticsearch.search.aggregations.AggregationBuilders.significantTerms;
-import static org.elasticsearch.test.VersionUtils.randomVersion;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.greaterThan;
@@ -69,9 +69,13 @@ public abstract class AbstractSignificanceHeuristicTestCase extends ESTestCase {
      */
      */
     protected abstract SignificanceHeuristic getHeuristic();
     protected abstract SignificanceHeuristic getHeuristic();
 
 
+    protected Version randomVersion() {
+        return VersionUtils.randomVersion(random());
+    }
+
     // test that stream output can actually be read - does not replace bwc test
     // test that stream output can actually be read - does not replace bwc test
     public void testStreamResponse() throws Exception {
     public void testStreamResponse() throws Exception {
-        Version version = randomVersion(random());
+        Version version = randomVersion();
         InternalMappedSignificantTerms<?, ?> sigTerms = getRandomSignificantTerms(getHeuristic());
         InternalMappedSignificantTerms<?, ?> sigTerms = getRandomSignificantTerms(getHeuristic());
 
 
         // write
         // write

+ 72 - 11
x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/aggs/heuristic/PValueScore.java

@@ -10,9 +10,11 @@ package org.elasticsearch.xpack.ml.aggs.heuristic;
 
 
 
 
 import org.apache.commons.math3.util.FastMath;
 import org.apache.commons.math3.util.FastMath;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.xcontent.ConstructingObjectParser;
 import org.elasticsearch.xcontent.ConstructingObjectParser;
+import org.elasticsearch.xcontent.ParseField;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.search.aggregations.AggregationExecutionException;
 import org.elasticsearch.search.aggregations.AggregationExecutionException;
@@ -20,47 +22,80 @@ import org.elasticsearch.search.aggregations.bucket.terms.heuristic.NXYSignifica
 import org.elasticsearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic;
 import org.elasticsearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.util.Objects;
 
 
 import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;
 import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;
 
 
+/**
+ * Significant terms heuristic that calculates the p-value between the term existing in foreground and background sets.
+ *
+ * The p-value is the probability of obtaining test results at least as extreme as
+ * the results actually observed, under the assumption that the null hypothesis is
+ * correct. The p-value is calculated assuming that the foreground set and the
+ * background set are independent https://en.wikipedia.org/wiki/Bernoulli_trial, with the null
+ * hypothesis that the probabilities are the same.
+ */
 public class PValueScore extends NXYSignificanceHeuristic {
 public class PValueScore extends NXYSignificanceHeuristic {
     public static final String NAME = "p_value";
     public static final String NAME = "p_value";
+    public static final ParseField NORMALIZE_ABOVE = new ParseField("normalize_above");
     public static final ConstructingObjectParser<PValueScore, Void> PARSER = new ConstructingObjectParser<>(NAME, args -> {
     public static final ConstructingObjectParser<PValueScore, Void> PARSER = new ConstructingObjectParser<>(NAME, args -> {
         boolean backgroundIsSuperset = args[0] == null || (boolean) args[0];
         boolean backgroundIsSuperset = args[0] == null || (boolean) args[0];
-        return new PValueScore(backgroundIsSuperset);
+        return new PValueScore(backgroundIsSuperset, (Long)args[1]);
     });
     });
     static {
     static {
         PARSER.declareBoolean(optionalConstructorArg(), BACKGROUND_IS_SUPERSET);
         PARSER.declareBoolean(optionalConstructorArg(), BACKGROUND_IS_SUPERSET);
+        PARSER.declareLong(optionalConstructorArg(), NORMALIZE_ABOVE);
     }
     }
 
 
     private static final MlChiSquaredDistribution CHI_SQUARED_DISTRIBUTION = new MlChiSquaredDistribution(1);
     private static final MlChiSquaredDistribution CHI_SQUARED_DISTRIBUTION = new MlChiSquaredDistribution(1);
 
 
-    public PValueScore(boolean backgroundIsSuperset) {
+    // NOTE: `0` is a magic value indicating no normalization occurs
+    private final long normalizeAbove;
+
+    /**
+     * @param backgroundIsSuperset Does the background contain the foreground docs?
+     * @param normalizeAbove Should the results be normalized when above the given value.
+     *                       Note: `0` is a special value which means no normalization (set as such when `null` is provided)
+     */
+    public PValueScore(boolean backgroundIsSuperset, Long normalizeAbove) {
         super(true, backgroundIsSuperset);
         super(true, backgroundIsSuperset);
+        if (normalizeAbove != null && normalizeAbove <= 0) {
+            throw new IllegalArgumentException(
+                "[" + NORMALIZE_ABOVE.getPreferredName() + "] must be a positive value, provided [" + normalizeAbove + "]"
+            );
+        }
+        this.normalizeAbove = normalizeAbove == null ? 0L : normalizeAbove;
     }
     }
 
 
     public PValueScore(StreamInput in) throws IOException {
     public PValueScore(StreamInput in) throws IOException {
         super(true, in.readBoolean());
         super(true, in.readBoolean());
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            normalizeAbove = in.readVLong();
+        } else {
+            normalizeAbove = 0L;
+        }
     }
     }
 
 
     @Override
     @Override
     public void writeTo(StreamOutput out) throws IOException {
     public void writeTo(StreamOutput out) throws IOException {
         out.writeBoolean(backgroundIsSuperset);
         out.writeBoolean(backgroundIsSuperset);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeVLong(normalizeAbove);
+        }
     }
     }
 
 
     @Override
     @Override
-    public boolean equals(Object obj) {
-        if ((obj instanceof PValueScore) == false) {
-            return false;
-        }
-        return super.equals(obj);
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (super.equals(o) == false) return false;
+        PValueScore that = (PValueScore) o;
+        return normalizeAbove == that.normalizeAbove;
     }
     }
 
 
     @Override
     @Override
     public int hashCode() {
     public int hashCode() {
-        int result = NAME.hashCode();
-        result = 31 * result + super.hashCode();
-        return result;
+        return Objects.hash(super.hashCode(), normalizeAbove);
     }
     }
 
 
     @Override
     @Override
@@ -72,6 +107,9 @@ public class PValueScore extends NXYSignificanceHeuristic {
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject(NAME);
         builder.startObject(NAME);
         builder.field(BACKGROUND_IS_SUPERSET.getPreferredName(), backgroundIsSuperset);
         builder.field(BACKGROUND_IS_SUPERSET.getPreferredName(), backgroundIsSuperset);
+        if (normalizeAbove > 0) {
+            builder.field(NORMALIZE_ABOVE.getPreferredName(), normalizeAbove);
+        }
         builder.endObject();
         builder.endObject();
         return builder;
         return builder;
     }
     }
@@ -113,6 +151,19 @@ public class PValueScore extends NXYSignificanceHeuristic {
             return 0.0;
             return 0.0;
         }
         }
 
 
+        if (normalizeAbove > 0L) {
+            if (allDocsInClass > normalizeAbove) {
+                double factor = (double) normalizeAbove / allDocsInClass;
+                allDocsInClass = (long)(allDocsInClass * factor);
+                docsContainTermInClass = (long)(docsContainTermInClass * factor);
+            }
+            if (allDocsNotInClass > normalizeAbove) {
+                double factor = (double) normalizeAbove / allDocsNotInClass;
+                allDocsNotInClass = (long)(allDocsNotInClass * factor);
+                docsContainTermNotInClass = (long)(docsContainTermNotInClass * factor);
+            }
+        }
+
         // casting to `long` to round down to nearest whole number
         // casting to `long` to round down to nearest whole number
         double epsAllDocsInClass = (long)eps(allDocsInClass);
         double epsAllDocsInClass = (long)eps(allDocsInClass);
         double epsAllDocsNotInClass = (long)eps(allDocsNotInClass);
         double epsAllDocsNotInClass = (long)eps(allDocsNotInClass);
@@ -164,15 +215,25 @@ public class PValueScore extends NXYSignificanceHeuristic {
     }
     }
 
 
     public static class PValueScoreBuilder extends NXYBuilder {
     public static class PValueScoreBuilder extends NXYBuilder {
+        private final long normalizeAbove;
 
 
-        public PValueScoreBuilder(boolean backgroundIsSuperset) {
+        public PValueScoreBuilder(boolean backgroundIsSuperset, Long normalizeAbove) {
             super(true, backgroundIsSuperset);
             super(true, backgroundIsSuperset);
+            this.normalizeAbove = normalizeAbove == null ? 0L : normalizeAbove;
+            if (normalizeAbove != null && normalizeAbove <= 0) {
+                throw new IllegalArgumentException(
+                    "[" + NORMALIZE_ABOVE.getPreferredName() + "] must be a positive value, provided [" + normalizeAbove + "]"
+                );
+            }
         }
         }
 
 
         @Override
         @Override
         public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
             builder.startObject(NAME);
             builder.startObject(NAME);
             builder.field(BACKGROUND_IS_SUPERSET.getPreferredName(), backgroundIsSuperset);
             builder.field(BACKGROUND_IS_SUPERSET.getPreferredName(), backgroundIsSuperset);
+            if (normalizeAbove > 0) {
+                builder.field(NORMALIZE_ABOVE.getPreferredName(), normalizeAbove);
+            }
             builder.endObject();
             builder.endObject();
             return builder;
             return builder;
         }
         }

+ 68 - 21
x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/aggs/heuristic/PValueScoreTests.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.xpack.ml.aggs.heuristic;
 package org.elasticsearch.xpack.ml.aggs.heuristic;
 
 
 import org.apache.commons.math3.util.FastMath;
 import org.apache.commons.math3.util.FastMath;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
@@ -29,19 +30,27 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
 
 
     private static final double eps = 1e-9;
     private static final double eps = 1e-9;
 
 
+    @Override
+    protected Version randomVersion() {
+        return Version.V_8_0_0;
+    }
+
     @Override
     @Override
     protected SignificanceHeuristic getHeuristic() {
     protected SignificanceHeuristic getHeuristic() {
-        return new PValueScore(randomBoolean());
+        return new PValueScore(randomBoolean(), randomBoolean() ? null : randomLongBetween(1, 10000000L));
     }
     }
 
 
     @Override
     @Override
     protected SignificanceHeuristic getHeuristic(boolean includeNegatives, boolean backgroundIsSuperset) {
     protected SignificanceHeuristic getHeuristic(boolean includeNegatives, boolean backgroundIsSuperset) {
-        return new PValueScore(backgroundIsSuperset);
+        return new PValueScore(backgroundIsSuperset, randomBoolean() ? null : randomLongBetween(1, 10000000L));
     }
     }
 
 
     @Override
     @Override
     public void testAssertions() {
     public void testAssertions() {
-        testBackgroundAssertions(new PValueScore(true), new PValueScore(false));
+        testBackgroundAssertions(
+            new PValueScore(true, randomBoolean() ? null : randomLongBetween(1, 10000000L)),
+            new PValueScore(false, randomBoolean() ? null : randomLongBetween(1, 10000000L))
+        );
     }
     }
 
 
     @Override
     @Override
@@ -59,7 +68,7 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
     }
     }
 
 
     public void testPValueScore_WhenAllDocsContainTerm() {
     public void testPValueScore_WhenAllDocsContainTerm() {
-        PValueScore pValueScore = new PValueScore(randomBoolean());
+        PValueScore pValueScore = new PValueScore(randomBoolean(), null);
         long supersetCount = randomNonNegativeLong();
         long supersetCount = randomNonNegativeLong();
         long subsetCount = randomLongBetween(0L, supersetCount);
         long subsetCount = randomLongBetween(0L, supersetCount);
         assertThat(pValueScore.getScore(subsetCount, subsetCount, supersetCount, supersetCount), equalTo(0.0));
         assertThat(pValueScore.getScore(subsetCount, subsetCount, supersetCount, supersetCount), equalTo(0.0));
@@ -78,7 +87,7 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
             supersetFreqCount += subsetFreqCount;
             supersetFreqCount += subsetFreqCount;
         }
         }
 
 
-        PValueScore pValueScore = new PValueScore(backgroundIsSuperset);
+        PValueScore pValueScore = new PValueScore(backgroundIsSuperset, null);
         assertThat(pValueScore.getScore(subsetFreqCount, subsetCount, supersetFreqCount, supersetCount), greaterThanOrEqualTo(700.0));
         assertThat(pValueScore.getScore(subsetFreqCount, subsetCount, supersetFreqCount, supersetCount), greaterThanOrEqualTo(700.0));
     }
     }
 
 
@@ -95,7 +104,7 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
             supersetFreqCount += subsetFreqCount;
             supersetFreqCount += subsetFreqCount;
         }
         }
 
 
-        PValueScore pValueScore = new PValueScore(backgroundIsSuperset);
+        PValueScore pValueScore = new PValueScore(backgroundIsSuperset, null);
         assertThat(
         assertThat(
             pValueScore.getScore(subsetFreqCount, subsetCount, supersetFreqCount, supersetCount),
             pValueScore.getScore(subsetFreqCount, subsetCount, supersetFreqCount, supersetCount),
             allOf(lessThanOrEqualTo(5.0), greaterThanOrEqualTo(0.0))
             allOf(lessThanOrEqualTo(5.0), greaterThanOrEqualTo(0.0))
@@ -104,66 +113,104 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
 
 
     public void testPValueScore() {
     public void testPValueScore() {
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(10, 100, 100, 1000)),
+            FastMath.exp(-new PValueScore(false, null).getScore(10, 100, 100, 1000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(10, 100, 10, 1000)),
+            FastMath.exp(-new PValueScore(false, 200L).getScore(10, 100, 100, 1000)),
+            closeTo(1.0, eps)
+        );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, null).getScore(10, 100, 10, 1000)),
             closeTo(0.003972388976814195, eps)
             closeTo(0.003972388976814195, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(10, 100, 200, 1000)),
+            FastMath.exp(-new PValueScore(false, 200L).getScore(10, 100, 10, 1000)),
+            closeTo(0.020890782016496683, eps)
+        );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, null).getScore(10, 100, 200, 1000)),
+            closeTo(1.0, eps)
+        );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, 200L).getScore(10, 100, 200, 1000)),
+            closeTo(1.0, eps)
+        );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, null).getScore(20, 10000, 5, 10000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(20, 10000, 5, 10000)),
+            FastMath.exp(-new PValueScore(false, 200L).getScore(20, 10000, 5, 10000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
     }
     }
 
 
     public void testSmallChanges() {
     public void testSmallChanges() {
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(1, 4205, 0, 821496)),
+            FastMath.exp(-new PValueScore(false, null).getScore(1, 4205, 0, 821496)),
             closeTo(0.9999037287868853, eps)
             closeTo(0.9999037287868853, eps)
         );
         );
+
         // Same(ish) ratios
         // Same(ish) ratios
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(10, 4205, 195, 82149)),
+            FastMath.exp(-new PValueScore(false, null).getScore(10, 4205, 195, 82149)),
             closeTo(0.9995943820612134, eps)
             closeTo(0.9995943820612134, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(10, 4205, 1950, 821496)),
+            FastMath.exp(-new PValueScore(false, 100L).getScore(10, 4205, 195, 82149)),
+            closeTo(0.9876284079864467, eps)
+        );
+
+        assertThat(
+            FastMath.exp(-new PValueScore(false, null).getScore(10, 4205, 1950, 821496)),
             closeTo(0.9999942565428899, eps)
             closeTo(0.9999942565428899, eps)
         );
         );
+        assertThat(
+            FastMath.exp(-new PValueScore(false,  100L).getScore(10, 4205, 1950, 821496)),
+            closeTo(1.0, eps)
+        );
 
 
         // 4% vs 0%
         // 4% vs 0%
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(168, 4205, 0, 821496)),
+            FastMath.exp(-new PValueScore(false, null).getScore(168, 4205, 0, 821496)),
             closeTo(1.2680918648731284e-26, eps)
             closeTo(1.2680918648731284e-26, eps)
         );
         );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, 100L).getScore(168, 4205, 0, 821496)),
+            closeTo(0.3882951183744724, eps)
+        );
         // 4% vs 2%
         // 4% vs 2%
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(168, 4205, 16429, 821496)),
+            FastMath.exp(-new PValueScore(false, null).getScore(168, 4205, 16429, 821496)),
             closeTo(8.542608559219833e-5, eps)
             closeTo(8.542608559219833e-5, eps)
         );
         );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, 100L).getScore(168, 4205, 16429, 821496)),
+            closeTo(0.579463586350363, eps)
+        );
         // 4% vs 3.5%
         // 4% vs 3.5%
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(168, 4205, 28752, 821496)),
+            FastMath.exp(-new PValueScore(false, null).getScore(168, 4205, 28752, 821496)),
             closeTo(0.8833950526957098, eps)
             closeTo(0.8833950526957098, eps)
         );
         );
+        assertThat(
+            FastMath.exp(-new PValueScore(false, 100L).getScore(168, 4205, 28752, 821496)),
+            closeTo(1.0, eps)
+        );
     }
     }
 
 
     public void testLargerValues() {
     public void testLargerValues() {
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(101000, 1000000, 500000, 5000000)),
+            FastMath.exp(-new PValueScore(false, null).getScore(101000, 1000000, 500000, 5000000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(102000, 1000000, 500000, 5000000)),
+            FastMath.exp(-new PValueScore(false, null).getScore(102000, 1000000, 500000, 5000000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
         assertThat(
         assertThat(
-            FastMath.exp(-new PValueScore(false).getScore(103000, 1000000, 500000, 5000000)),
+            FastMath.exp(-new PValueScore(false, null).getScore(103000, 1000000, 500000, 5000000)),
             closeTo(1.0, eps)
             closeTo(1.0, eps)
         );
         );
     }
     }
@@ -171,7 +218,7 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
     public void testScoreIsZero() {
     public void testScoreIsZero() {
         for (int j = 0; j < 10; j++) {
         for (int j = 0; j < 10; j++) {
             assertThat(
             assertThat(
-                new PValueScore(false).getScore((j + 1)*5, (j + 10)*100, (j + 1)*10, (j + 10)*100),
+                new PValueScore(false, null).getScore((j + 1)*5, (j + 10)*100, (j + 1)*10, (j + 10)*100),
                 equalTo(0.0)
                 equalTo(0.0)
             );
             );
         }
         }
@@ -179,7 +226,7 @@ public class PValueScoreTests extends AbstractNXYSignificanceHeuristicTestCase {
 
 
     public void testIncreasedSubsetIncreasedScore() {
     public void testIncreasedSubsetIncreasedScore() {
         final Function<Long, Double> getScore = (subsetFreq) ->
         final Function<Long, Double> getScore = (subsetFreq) ->
-            new PValueScore(false).getScore(subsetFreq, 5000, 5, 5000);
+            new PValueScore(false, null).getScore(subsetFreq, 5000, 5, 5000);
         double priorScore = getScore.apply(5L);
         double priorScore = getScore.apply(5L);
         assertThat(priorScore, greaterThanOrEqualTo(0.0));
         assertThat(priorScore, greaterThanOrEqualTo(0.0));
         for (int j = 1; j < 11; j++) {
         for (int j = 1; j < 11; j++) {