Skip to content

Commit 1f07cca

Browse files
committed
Simplify summing up algorithm for aggregations
1 parent 15cdec2 commit 1f07cca

File tree

9 files changed

+72
-55
lines changed

9 files changed

+72
-55
lines changed

core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgAggregator.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,8 @@ public void collect(int doc, long bucket) throws IOException {
9090

9191
for (int i = 0; i < valueCount; i++) {
9292
double value = values.nextValue();
93-
if (Double.isNaN(value) || Double.isInfinite(value)) {
93+
if (Double.isFinite(value) == false) {
9494
sum += value;
95-
if (Double.isNaN(sum))
96-
break;
9795
} else if (Double.isFinite(sum)) {
9896
double corrected = value - compensation;
9997
double newSum = sum + corrected;

core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvg.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,13 @@ public InternalAvg doReduce(List<InternalAggregation> aggregations, ReduceContex
9797
for (InternalAggregation aggregation : aggregations) {
9898
InternalAvg avg = (InternalAvg) aggregation;
9999
count += avg.count;
100-
if (Double.isNaN(sum) == false) {
101-
if (Double.isNaN(avg.sum) || Double.isInfinite(avg.sum)) {
102-
sum += avg.sum;
103-
} else if (Double.isFinite(sum)) {
104-
double corrected = avg.sum - compensation;
105-
double newSum = sum + corrected;
106-
compensation = (newSum - sum) - corrected;
107-
sum = newSum;
108-
}
100+
if (Double.isFinite(avg.sum) == false) {
101+
sum += avg.sum;
102+
} else if (Double.isFinite(sum)) {
103+
double corrected = avg.sum - compensation;
104+
double newSum = sum + corrected;
105+
compensation = (newSum - sum) - corrected;
106+
sum = newSum;
109107
}
110108
}
111109
return new InternalAvg(getName(), sum, count, format, pipelineAggregators(), getMetaData());

core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,14 @@ public InternalStats doReduce(List<InternalAggregation> aggregations, ReduceCont
160160
max = Math.max(max, stats.getMax());
161161
// Compute the sum of double values with Kahan summation algorithm which is more
162162
// accurate than naive summation.
163-
if (Double.isNaN(sum) == false) {
164-
double value = stats.getSum();
165-
if (Double.isNaN(value) || Double.isInfinite(value)) {
166-
sum += value;
167-
} else if (Double.isFinite(sum)) {
168-
double corrected = value - compensation;
169-
double newSum = sum + corrected;
170-
compensation = (newSum - sum) - corrected;
171-
sum = newSum;
172-
}
163+
double value = stats.getSum();
164+
if (Double.isFinite(value) == false) {
165+
sum += value;
166+
} else if (Double.isFinite(sum)) {
167+
double corrected = value - compensation;
168+
double newSum = sum + corrected;
169+
compensation = (newSum - sum) - corrected;
170+
sum = newSum;
173171
}
174172
}
175173
return new InternalStats(name, count, sum, min, max, format, pipelineAggregators(), getMetaData());

core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsAggregator.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ public class StatsAggregator extends NumericMetricsAggregator.MultiValue {
4545

4646
LongArray counts;
4747
DoubleArray sums;
48+
DoubleArray compensations;
4849
DoubleArray mins;
4950
DoubleArray maxes;
5051

51-
private DoubleArray compensations;
52-
5352

5453
public StatsAggregator(String name, ValuesSource.Numeric valuesSource, DocValueFormat format,
5554
SearchContext context,
@@ -110,15 +109,13 @@ public void collect(int doc, long bucket) throws IOException {
110109

111110
for (int i = 0; i < valuesCount; i++) {
112111
double value = values.nextValue();
113-
if (Double.isNaN(sum) == false) {
114-
if (Double.isNaN(value) || Double.isInfinite(value)) {
115-
sum += value;
116-
} else if (Double.isFinite(sum)) {
117-
double corrected = value - compensation;
118-
double newSum = sum + corrected;
119-
compensation = (newSum - sum) - corrected;
120-
sum = newSum;
121-
}
112+
if (Double.isFinite(value) == false) {
113+
sum += value;
114+
} else if (Double.isFinite(sum)) {
115+
double corrected = value - compensation;
116+
double newSum = sum + corrected;
117+
compensation = (newSum - sum) - corrected;
118+
sum = newSum;
122119
}
123120
min = Math.min(min, value);
124121
max = Math.max(max, value);

core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsAggregator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void collect(int doc, long bucket) throws IOException {
121121
double compensationOfSqr = compensationOfSqrs.get(bucket);
122122
for (int i = 0; i < valuesCount; i++) {
123123
double value = values.nextValue();
124-
if (Double.isNaN(value) || Double.isInfinite(value)) {
124+
if (Double.isFinite(value) == false) {
125125
sum += value;
126126
sumOfSqr += value * value;
127127
} else {

core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static Metrics resolve(String name) {
4545
private final double sigma;
4646

4747
public InternalExtendedStats(String name, long count, double sum, double min, double max, double sumOfSqrs, double sigma,
48-
DocValueFormat formatter, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) {
48+
DocValueFormat formatter, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) {
4949
super(name, count, sum, min, max, formatter, pipelineAggregators, metaData);
5050
this.sumOfSqrs = sumOfSqrs;
5151
this.sigma = sigma;
@@ -148,21 +148,19 @@ public InternalExtendedStats doReduce(List<InternalAggregation> aggregations, Re
148148
if (stats.sigma != sigma) {
149149
throw new IllegalStateException("Cannot reduce other stats aggregations that have a different sigma");
150150
}
151-
if (Double.isNaN(sumOfSqrs) == false) {
152-
double value = stats.getSumOfSquares();
153-
if (Double.isNaN(value) || Double.isInfinite(value)) {
154-
sumOfSqrs += value;
155-
} else if (Double.isFinite(sumOfSqrs)) {
156-
double correctedOfSqrs = value - compensationOfSqrs;
157-
double newSumOfSqrs = sumOfSqrs + correctedOfSqrs;
158-
compensationOfSqrs = (newSumOfSqrs - sumOfSqrs) - correctedOfSqrs;
159-
sumOfSqrs = newSumOfSqrs;
160-
}
151+
double value = stats.getSumOfSquares();
152+
if (Double.isFinite(value) == false) {
153+
sumOfSqrs += value;
154+
} else if (Double.isFinite(sumOfSqrs)) {
155+
double correctedOfSqrs = value - compensationOfSqrs;
156+
double newSumOfSqrs = sumOfSqrs + correctedOfSqrs;
157+
compensationOfSqrs = (newSumOfSqrs - sumOfSqrs) - correctedOfSqrs;
158+
sumOfSqrs = newSumOfSqrs;
161159
}
162160
}
163161
final InternalStats stats = super.doReduce(aggregations, reduceContext);
164162
return new InternalExtendedStats(name, stats.getCount(), stats.getSum(), stats.getMin(), stats.getMax(), sumOfSqrs, sigma,
165-
format, pipelineAggregators(), getMetaData());
163+
format, pipelineAggregators(), getMetaData());
166164
}
167165

168166
static class Fields {

core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSum.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,8 @@ public InternalSum doReduce(List<InternalAggregation> aggregations, ReduceContex
7979
double compensation = 0;
8080
for (InternalAggregation aggregation : aggregations) {
8181
double value = ((InternalSum) aggregation).sum;
82-
if (Double.isNaN(value) || Double.isInfinite(value)) {
82+
if (Double.isFinite(value) == false) {
8383
sum += value;
84-
if (Double.isNaN(sum))
85-
break;
8684
} else if (Double.isFinite(sum)) {
8785
double corrected = value - compensation;
8886
double newSum = sum + corrected;

core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumAggregator.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,8 @@ public void collect(int doc, long bucket) throws IOException {
8383
double compensation = compensations.get(bucket);
8484
for (int i = 0; i < valuesCount; i++) {
8585
double value = values.nextValue();
86-
if (Double.isNaN(value) || Double.isInfinite(value)) {
86+
if (Double.isFinite(value) == false) {
8787
sum += value;
88-
if (Double.isNaN(sum))
89-
break;
9088
} else if (Double.isFinite(sum)) {
9189
double corrected = value - compensation;
9290
double newSum = sum + corrected;

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

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535

3636
public class InternalSumTests extends InternalAggregationTestCase<InternalSum> {
3737

38+
private static final double TOLERANCE = 1e-10;
39+
3840
@Override
3941
protected InternalSum createTestInstance(String name, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) {
40-
double value = frequently() ? randomDouble() : randomFrom(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
42+
double value = frequently() ? randomDouble() : randomFrom(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN);
4143
DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW);
4244
return new InternalSum(name, value, formatter, pipelineAggregators, metaData);
4345
}
@@ -54,15 +56,45 @@ protected void assertReduced(InternalSum reduced, List<InternalSum> inputs) {
5456
}
5557

5658
public void testSummationAccuracy() throws IOException {
59+
// Summing up a normal array and expect an accurate value
5760
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);
62+
63+
// Summing up an array which contains NaN and infinities and expect a result same as naive summation
64+
int n = randomIntBetween(5, 10);
65+
values = new double[n];
66+
double sum = 0;
67+
for (int i = 0; i < n; i++) {
68+
values[i] = frequently()
69+
? randomFrom(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)
70+
: randomDoubleBetween(Double.MIN_VALUE, Double.MAX_VALUE, true);
71+
sum += values[i];
72+
}
73+
verifySummationOfDoubles(values, sum, TOLERANCE);
74+
75+
// Summing up some big double values and expect infinity result
76+
n = randomIntBetween(5, 10);
77+
double[] bigPositiveDoubles = new double[n];
78+
for (int i = 0; i < n; i++) {
79+
bigPositiveDoubles[i] = Double.MAX_VALUE;
80+
}
81+
verifySummationOfDoubles(bigPositiveDoubles, Double.POSITIVE_INFINITY, 0d);
82+
83+
double[] bigNegativeDoubles = new double[n];
84+
for (int i = 0; i < n; i++) {
85+
bigNegativeDoubles[i] = -Double.MAX_VALUE;
86+
}
87+
verifySummationOfDoubles(bigNegativeDoubles, Double.NEGATIVE_INFINITY, 0d);
88+
}
89+
90+
private void verifySummationOfDoubles(double[] values, double expected, double delta) throws IOException {
5891
List<InternalAggregation> aggregations = new ArrayList<>(values.length);
5992
for (double value : values) {
6093
aggregations.add(new InternalSum("dummy1", value, null, null, null));
6194
}
6295
InternalSum internalSum = new InternalSum("dummy", 0, null, null, null);
6396
InternalSum reduced = internalSum.doReduce(aggregations, null);
64-
assertEquals(13.5, reduced.value(), 0d);
65-
assertEquals("dummy", reduced.getName());
97+
assertEquals(expected, reduced.value(), delta);
6698
}
6799

68100
@Override

0 commit comments

Comments
 (0)