Skip to content

Commit 176be1f

Browse files
committed
Add test case for summing up NaN and infinities
1 parent b2ce7cb commit 176be1f

File tree

9 files changed

+279
-67
lines changed

9 files changed

+279
-67
lines changed

server/src/test/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsAggregatorTests.java

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
package org.elasticsearch.search.aggregations.metrics;
2121

2222
import org.apache.lucene.document.Document;
23-
import org.apache.lucene.document.DoubleDocValuesField;
23+
import org.apache.lucene.document.NumericDocValuesField;
2424
import org.apache.lucene.document.SortedNumericDocValuesField;
2525
import org.apache.lucene.index.IndexReader;
2626
import org.apache.lucene.index.RandomIndexWriter;
@@ -136,34 +136,65 @@ public void testRandomLongs() throws IOException {
136136
}
137137

138138
public void testSummationAccuracy() throws IOException {
139+
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
140+
verifyStatsOfDoubles(values, 13.5, 16.21, 0d);
141+
142+
// Summing up an array which contains NaN and infinities and expect a result same as naive summation
143+
int n = randomIntBetween(5, 10);
144+
values = new double[n];
145+
double sum = 0;
146+
double sumOfSqrs = 0;
147+
for (int i = 0; i < n; i++) {
148+
values[i] = frequently()
149+
? randomFrom(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
150+
: randomDoubleBetween(Double.MIN_VALUE, Double.MAX_VALUE, true);
151+
sum += values[i];
152+
sumOfSqrs += values[i] * values[i];
153+
}
154+
verifyStatsOfDoubles(values, sum, sumOfSqrs, TOLERANCE);
155+
156+
// Summing up some big double values and expect infinity result
157+
n = randomIntBetween(5, 10);
158+
double[] largeValues = new double[n];
159+
for (int i = 0; i < n; i++) {
160+
largeValues[i] = Double.MAX_VALUE;
161+
}
162+
verifyStatsOfDoubles(largeValues, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0d);
163+
164+
for (int i = 0; i < n; i++) {
165+
largeValues[i] = -Double.MAX_VALUE;
166+
}
167+
verifyStatsOfDoubles(largeValues, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0d);
168+
}
169+
170+
private void verifyStatsOfDoubles(double[] values, double expectedSum,
171+
double expectedSumOfSqrs, double delta) throws IOException {
139172
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.DOUBLE);
140173
final String fieldName = "field";
141174
ft.setName(fieldName);
175+
double max = Double.NEGATIVE_INFINITY;
176+
double min = Double.POSITIVE_INFINITY;
177+
for (double value : values) {
178+
max = Math.max(max, value);
179+
min = Math.min(min, value);
180+
}
181+
double expectedMax = max;
182+
double expectedMin = min;
142183
testCase(ft,
143184
iw -> {
144-
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
145185
for (double value : values) {
146-
iw.addDocument(singleton(new DoubleDocValuesField(fieldName, value)));
186+
iw.addDocument(singleton(new NumericDocValuesField(fieldName, NumericUtils.doubleToSortableLong(value))));
147187
}
148188
},
149189
stats -> {
150-
assertEquals(15, stats.getCount());
151-
assertEquals(0.9, stats.getAvg(), 0d);
152-
assertEquals(13.5, stats.getSum(), 0d);
153-
assertEquals(1.7, stats.getMax(), 0d);
154-
assertEquals(0.1, stats.getMin(), 0d);
155-
assertEquals(0.1, stats.getMin(), 0d);
190+
assertEquals(values.length, stats.getCount());
191+
assertEquals(expectedSum / values.length, stats.getAvg(), delta);
192+
assertEquals(expectedSum, stats.getSum(), delta);
193+
assertEquals(expectedSumOfSqrs, stats.getSumOfSquares(), delta);
194+
assertEquals(expectedMax, stats.getMax(), 0d);
195+
assertEquals(expectedMin, stats.getMin(), 0d);
156196
}
157197
);
158-
testCase(ft,
159-
iw -> {
160-
double[] values = new double[]{2.1, 0.4, 0.4, 0.5, 0.5, 0.7, 0.9, 1.001, 1.222, 1.3, 1.4, 1.5, 1.6, 1.9};
161-
for (double value : values) {
162-
iw.addDocument(singleton(new DoubleDocValuesField(fieldName, value)));
163-
}
164-
},
165-
stats -> assertEquals(21.095285, stats.getSumOfSquares(), 0d)
166-
);
167198
}
168199

169200
public void testCase(MappedFieldType ft,

server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalExtendedStatsTests.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,41 @@ protected InternalExtendedStats mutateInstance(InternalExtendedStats instance) {
193193

194194
public void testSummationAccuracy() {
195195
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
196-
double sigma = randomDouble();
196+
verifySumOfSqrsOfDoubles(values, 13.5, 0d);
197+
198+
int n = randomIntBetween(5, 10);
199+
values = new double[n];
200+
double sum = 0;
201+
for (int i = 0; i < n; i++) {
202+
values[i] = frequently()
203+
? randomFrom(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
204+
: randomDoubleBetween(Double.MIN_VALUE, Double.MAX_VALUE, true);
205+
sum += values[i];
206+
}
207+
verifySumOfSqrsOfDoubles(values, sum, TOLERANCE);
208+
209+
// Summing up some big double values and expect infinity result
210+
n = randomIntBetween(5, 10);
211+
double[] largeValues = new double[n];
212+
for (int i = 0; i < n; i++) {
213+
largeValues[i] = Double.MAX_VALUE;
214+
}
215+
verifySumOfSqrsOfDoubles(largeValues, Double.POSITIVE_INFINITY, 0d);
216+
217+
for (int i = 0; i < n; i++) {
218+
largeValues[i] = -Double.MAX_VALUE;
219+
}
220+
verifySumOfSqrsOfDoubles(largeValues, Double.NEGATIVE_INFINITY, 0d);
221+
}
222+
223+
private void verifySumOfSqrsOfDoubles(double[] values, double expectedSumOfSqrs, double delta) {
197224
List<InternalAggregation> aggregations = new ArrayList<>(values.length);
225+
double sigma = randomDouble();
198226
for (double sumOfSqrs : values) {
199-
aggregations.add(new InternalExtendedStats("dummy1", 1, 0.0, 0.0, 0.0, sumOfSqrs, sigma, null, null, null));
227+
aggregations.add(new InternalExtendedStats("dummy1", 1, 0.0, 0.0, 0.0, sumOfSqrs, sigma, null, null, null));
200228
}
201-
InternalExtendedStats stats = new InternalExtendedStats("dummy", 1, 0.0, 0.0, 0.0, 0.0, sigma, null, null, null);
229+
InternalExtendedStats stats = new InternalExtendedStats("dummy", 1, 0.0, 0.0, 0.0, 0.0, sigma, null, null, null);
202230
InternalExtendedStats reduced = stats.doReduce(aggregations, null);
203-
assertEquals(13.5, reduced.getSumOfSquares(), 0d);
204-
assertEquals("dummy", reduced.getName());
231+
assertEquals(expectedSumOfSqrs, reduced.getSumOfSquares(), delta);
205232
}
206233
}

server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,52 @@ protected void assertReduced(InternalStats reduced, List<InternalStats> inputs)
7676
assertEquals(expectedMax, reduced.getMax(), 0d);
7777
}
7878

79-
public void testSummationAccuracy() throws IOException {
79+
public void testSummationAccuracy() {
8080
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
81+
verifyStatsOfDoubles(values, 13.5, 0.9, 0d);
82+
83+
int n = randomIntBetween(5, 10);
84+
values = new double[n];
85+
double sum = 0;
86+
for (int i = 0; i < n; i++) {
87+
values[i] = frequently()
88+
? randomFrom(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
89+
: randomDoubleBetween(Double.MIN_VALUE, Double.MAX_VALUE, true);
90+
sum += values[i];
91+
}
92+
verifyStatsOfDoubles(values, sum, sum / n, TOLERANCE);
93+
94+
// Summing up some big double values and expect infinity result
95+
n = randomIntBetween(5, 10);
96+
double[] largeValues = new double[n];
97+
for (int i = 0; i < n; i++) {
98+
largeValues[i] = Double.MAX_VALUE;
99+
}
100+
verifyStatsOfDoubles(largeValues, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0d);
101+
102+
for (int i = 0; i < n; i++) {
103+
largeValues[i] = -Double.MAX_VALUE;
104+
}
105+
verifyStatsOfDoubles(largeValues, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0d);
106+
}
107+
108+
private void verifyStatsOfDoubles(double[] values, double expectedSum, double expectedAvg, double delta) {
81109
List<InternalAggregation> aggregations = new ArrayList<>(values.length);
110+
double max = Double.NEGATIVE_INFINITY;
111+
double min = Double.POSITIVE_INFINITY;
82112
for (double value : values) {
113+
max = Math.max(max, value);
114+
min = Math.min(min, value);
83115
aggregations.add(new InternalStats("dummy1", 1, value, value, value, null, null, null));
84116
}
85117
InternalStats internalStats = new InternalStats("dummy2", 0, 0.0, 2.0, 0.0, null, null, null);
86118
InternalStats reduced = internalStats.doReduce(aggregations, null);
87119
assertEquals("dummy2", reduced.getName());
88120
assertEquals(values.length, reduced.getCount());
89-
assertEquals(13.5, reduced.getSum(), 0d);
90-
assertEquals(0.9, reduced.getAvg(), 0d);
91-
assertEquals(0.1, reduced.getMin(), 0d);
92-
assertEquals(1.7, reduced.getMax(), 0d);
121+
assertEquals(expectedSum, reduced.getSum(), delta);
122+
assertEquals(expectedAvg, reduced.getAvg(), delta);
123+
assertEquals(min, reduced.getMin(), 0d);
124+
assertEquals(max, reduced.getMax(), 0d);
93125
}
94126

95127
@Override

server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalSumTests.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,13 @@
2727
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
2828
import org.elasticsearch.test.InternalAggregationTestCase;
2929

30-
import java.io.IOException;
3130
import java.util.ArrayList;
3231
import java.util.HashMap;
3332
import java.util.List;
3433
import java.util.Map;
3534

3635
public class InternalSumTests extends InternalAggregationTestCase<InternalSum> {
3736

38-
private static final double TOLERANCE = 1e-10;
39-
4037
@Override
4138
protected InternalSum createTestInstance(String name, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) {
4239
double value = frequently() ? randomDouble() : randomFrom(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN);
@@ -55,10 +52,10 @@ protected void assertReduced(InternalSum reduced, List<InternalSum> inputs) {
5552
assertEquals(expectedSum, reduced.getValue(), 0.0001d);
5653
}
5754

58-
public void testSummationAccuracy() throws IOException {
55+
public void testSummationAccuracy() {
5956
// Summing up a normal array and expect an accurate value
6057
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
61-
verifySummationOfDoubles(values, 13.5, 0);
58+
verifySummationOfDoubles(values, 13.5, 0d);
6259

6360
// Summing up an array which contains NaN and infinities and expect a result same as naive summation
6461
int n = randomIntBetween(5, 10);
@@ -74,20 +71,19 @@ public void testSummationAccuracy() throws IOException {
7471

7572
// Summing up some big double values and expect infinity result
7673
n = randomIntBetween(5, 10);
77-
double[] bigPositiveDoubles = new double[n];
74+
double[] largeValues = new double[n];
7875
for (int i = 0; i < n; i++) {
79-
bigPositiveDoubles[i] = Double.MAX_VALUE;
76+
largeValues[i] = Double.MAX_VALUE;
8077
}
81-
verifySummationOfDoubles(bigPositiveDoubles, Double.POSITIVE_INFINITY, 0d);
78+
verifySummationOfDoubles(largeValues, Double.POSITIVE_INFINITY, 0d);
8279

83-
double[] bigNegativeDoubles = new double[n];
8480
for (int i = 0; i < n; i++) {
85-
bigNegativeDoubles[i] = -Double.MAX_VALUE;
81+
largeValues[i] = -Double.MAX_VALUE;
8682
}
87-
verifySummationOfDoubles(bigNegativeDoubles, Double.NEGATIVE_INFINITY, 0d);
83+
verifySummationOfDoubles(largeValues, Double.NEGATIVE_INFINITY, 0d);
8884
}
8985

90-
private void verifySummationOfDoubles(double[] values, double expected, double delta) throws IOException {
86+
private void verifySummationOfDoubles(double[] values, double expected, double delta) {
9187
List<InternalAggregation> aggregations = new ArrayList<>(values.length);
9288
for (double value : values) {
9389
aggregations.add(new InternalSum("dummy1", value, null, null, null));

server/src/test/java/org/elasticsearch/search/aggregations/metrics/StatsAggregatorTests.java

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
package org.elasticsearch.search.aggregations.metrics;
2020

2121
import org.apache.lucene.document.Document;
22-
import org.apache.lucene.document.DoubleDocValuesField;
22+
import org.apache.lucene.document.NumericDocValuesField;
2323
import org.apache.lucene.document.SortedNumericDocValuesField;
2424
import org.apache.lucene.index.IndexReader;
2525
import org.apache.lucene.index.RandomIndexWriter;
@@ -117,22 +117,61 @@ public void testRandomLongs() throws IOException {
117117
}
118118

119119
public void testSummationAccuracy() throws IOException {
120+
// Summing up a normal array and expect an accurate value
121+
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
122+
verifySummationOfDoubles(values, 15.3, 0.9, 0d);
123+
124+
// Summing up an array which contains NaN and infinities and expect a result same as naive summation
125+
int n = randomIntBetween(5, 10);
126+
values = new double[n];
127+
double sum = 0;
128+
for (int i = 0; i < n; i++) {
129+
values[i] = frequently()
130+
? randomFrom(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
131+
: randomDoubleBetween(Double.MIN_VALUE, Double.MAX_VALUE, true);
132+
sum += values[i];
133+
}
134+
verifySummationOfDoubles(values, sum, sum / n, TOLERANCE);
135+
136+
// Summing up some big double values and expect infinity result
137+
n = randomIntBetween(5, 10);
138+
double[] largeValues = new double[n];
139+
for (int i = 0; i < n; i++) {
140+
largeValues[i] = Double.MAX_VALUE;
141+
}
142+
verifySummationOfDoubles(largeValues, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0d);
143+
144+
for (int i = 0; i < n; i++) {
145+
largeValues[i] = -Double.MAX_VALUE;
146+
}
147+
verifySummationOfDoubles(largeValues, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0d);
148+
}
149+
150+
private void verifySummationOfDoubles(double[] values, double expectedSum,
151+
double expectedAvg, double delta) throws IOException {
120152
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.DOUBLE);
121-
final String fieldName = "field";
122-
ft.setName(fieldName);
153+
ft.setName("field");
154+
155+
double max = Double.NEGATIVE_INFINITY;
156+
double min = Double.POSITIVE_INFINITY;
157+
for (double value : values) {
158+
max = Math.max(max, value);
159+
min = Math.min(min, value);
160+
}
161+
double expectedMax = max;
162+
double expectedMin = min;
123163
testCase(ft,
124164
iw -> {
125-
double[] values = new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
126165
for (double value : values) {
127-
iw.addDocument(singleton(new DoubleDocValuesField(fieldName, value)));
166+
iw.addDocument(singleton(new NumericDocValuesField("field", NumericUtils.doubleToSortableLong(value))));
128167
}
129168
},
130169
stats -> {
131-
assertEquals(15, stats.getCount());
132-
assertEquals(0.9, stats.getAvg(), 0d);
133-
assertEquals(13.5, stats.getSum(), 0d);
134-
assertEquals(1.7, stats.getMax(), 0d);
135-
assertEquals(0.1, stats.getMin(), 0d);
170+
assertEquals(values.length, stats.getCount());
171+
assertEquals(expectedAvg, stats.getAvg(), delta);
172+
assertEquals(expectedSum, stats.getSum(), delta);
173+
assertEquals(expectedMax, stats.getMax(), 0d);
174+
assertEquals(expectedMin, stats.getMin(), 0d);
136175
}
137176
);
138177
}

0 commit comments

Comments
 (0)