blob: 1618f81e79419448f951fed145c29ee4bbb414de [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.datasketches.hll;
import static org.apache.datasketches.common.Util.pwr2SeriesNext;
import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
/**
* This test demonstrates that DataSketch HLL merging is not order-dependent if you use the Composite estimator.
*/
public class HllSketchMergeOrderTest {
private static final int LgK = 11;
@Test
public void testDataSketchHLLMergeOrderDependency() {
final int ppo = 1 << 17; //= 131,072 unique Points Per Octave.
// Create 3 sketches with fractional powers of 2 series
final HllSketch sketchA = createUniquePowerSeriesSketch(1L << 59, ppo, 1L << 60);
final HllSketch sketchB = createUniquePowerSeriesSketch(1L << 60, ppo, 1L << 61);
final HllSketch sketchC = createUniquePowerSeriesSketch(1L << 61, ppo, 1L << 62);
final double skAEst = sketchA.getCompositeEstimate();
final double skBEst = sketchB.getCompositeEstimate();
final double skCEst = sketchC.getCompositeEstimate();
final double skA_RE = (skAEst/ppo) - 1.0;
final double skB_RE = (skBEst/ppo) - 1.0;
final double skC_RE = (skCEst/ppo) - 1.0;
//Print individual composite estimates and Relative Error:
println("");
println("SketchA estimate: " + skAEst + ", RE%: " + (skA_RE * 100));
println("SketchB estimate: " + skBEst + ", RE%: " + (skB_RE * 100));
println("SketchC estimate: " + skCEst + ", RE%: " + (skC_RE * 100));
println("\nNOTE: Sketch Relative Error for Composite Estimator for LgK = 11 is +/- 4.6% at 95% confidence.\n");
// Test six different merge orders:
final double estABC = mergeThreeSketches(sketchA, sketchB, sketchC);
final double estACB = mergeThreeSketches(sketchA, sketchC, sketchB);
final double estBAC = mergeThreeSketches(sketchB, sketchA, sketchC);
final double estBCA = mergeThreeSketches(sketchB, sketchC, sketchA);
final double estCAB = mergeThreeSketches(sketchC, sketchA, sketchB);
final double estCBA = mergeThreeSketches(sketchC, sketchB, sketchA);
println("Merge order ABC: " + estABC);
println("Merge order ACB: " + estACB);
println("Merge order BAC: " + estBAC);
println("Merge order BCA: " + estBCA);
println("Merge order CAB: " + estCAB);
println("Merge order CBA: " + estCBA);
assertTrue((estABC == estACB) && (estABC == estBAC) && (estABC == estBCA) && (estABC == estCAB) && (estABC == estCBA));
}
/**
* Generates a power series based on fractional powers of 2 where the separation between successive values is 2^(1/ppo).
* @param baseValue starting value, inclusive
* @param ppo number of unique points per octave
* @param limit the upper limit, exclusive
* @return the loaded sketch
*/
private HllSketch createUniquePowerSeriesSketch(final long baseValue, final int ppo, final long limit) {
final HllSketch sketch = new HllSketch(LgK);
int count = 0;
long lastp = 0;
for (long p = baseValue; p < limit; p = pwr2SeriesNext(ppo, p)) {
sketch.update(p);
count++;
lastp = p;
}
println("BaseValue: " + baseValue + ", limit: " + limit + ", Count: " + count + ", lastPt: " + lastp);
return sketch;
}
/**
* Merges three sketches in the specified order and returns the composite estimate
*/
private double mergeThreeSketches(final HllSketch s1, final HllSketch s2, final HllSketch s3) {
final HllUnion union = new HllUnion(LgK);
union.update(s1);
union.update(s2);
union.update(s3);
return union.getCompositeEstimate();
}
//@Test
/**
* Used for tweaking the generator algorithm
*/
public void checkNewGenerator() {
final long baseValue = 1L << 59;
final int ppo = 1 << 10;
final long limit = 1L << 60;
int count = 0;
long lastp = 0;
for (long p = baseValue; p < limit; p = pwr2SeriesNext(ppo, p)) {
count++;
println(count + ", " + p);
lastp = p;
}
println("\nPPO: " + ppo);
println("Count: " + count);
println("baseValue: " + baseValue);
println("last p: " + lastp);
println("limit: " + limit);
}
private static void println(final Object o) {
//System.out.println(o.toString());
}
}