blob: 99b261c2a40b6c7226e2ec23a972fed2c498b9e5 [file] [log] [blame]
/*
* 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.hll.CurMode.HLL;
import static org.apache.datasketches.hll.CurMode.LIST;
import static org.apache.datasketches.hll.CurMode.SET;
import static org.apache.datasketches.hll.TgtHllType.HLL_4;
import static org.apache.datasketches.hll.TgtHllType.HLL_6;
import static org.apache.datasketches.hll.TgtHllType.HLL_8;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import org.apache.datasketches.memory.WritableMemory;
import org.testng.annotations.Test;
/**
* @author Lee Rhodes
*/
@SuppressWarnings({"javadoc", "unused"})
public class IsomorphicTest {
long v = 0;
@Test
//Merges a type1 to an empty union (heap, HLL_8), and gets result as type1, checks binary equivalence
public void isomorphicUnionUpdatableHeap() {
for (int lgK = 4; lgK <= 21; lgK++) { //All LgK
for (int cm = 0; cm <= 2; cm++) { //List, Set, Hll
if ((lgK < 8) && (cm == 1)) { continue; } //lgk < 8 list transistions directly to HLL
CurMode curMode = CurMode.fromOrdinal(cm);
for (int t = 0; t <= 2; t++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType1 = TgtHllType.fromOrdinal(t);
HllSketch sk1 = buildHeapSketch(lgK, tgtHllType1, curMode);
byte[] sk1bytes = sk1.toUpdatableByteArray(); //UPDATABLE
Union union = buildHeapUnion(lgK, null); //UNION
union.update(sk1);
HllSketch sk2 = union.getResult(tgtHllType1);
byte[] sk2bytes = sk2.toUpdatableByteArray(); //UPDATABLE
String comb = "LgK=" + lgK + ", CurMode=" + curMode.toString() + ", Type:" + tgtHllType1;
checkArrays(sk1bytes, sk2bytes, comb, false);
}
}
}
}
@Test
//Merges a type1 to an empty union (heap, HLL_8), and gets result as type1, checks binary equivalence
public void isomorphicUnionCompactHeap() {
for (int lgK = 4; lgK <= 21; lgK++) { //All LgK
for (int cm = 0; cm <= 2; cm++) { //List, Set, Hll
if ((lgK < 8) && (cm == 1)) { continue; } //lgk < 8 list transistions directly to HLL
CurMode curMode = CurMode.fromOrdinal(cm);
for (int t = 0; t <= 2; t++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType1 = TgtHllType.fromOrdinal(t);
HllSketch sk1 = buildHeapSketch(lgK, tgtHllType1, curMode);
byte[] sk1bytes = sk1.toCompactByteArray(); //COMPACT
Union union = buildHeapUnion(lgK, null); //UNION
union.update(sk1);
HllSketch sk2 = union.getResult(tgtHllType1);
byte[] sk2bytes = sk2.toCompactByteArray(); //COMPACT
String comb = "LgK=" + lgK + ", CurMode=" + curMode.toString() + ", Type:" + tgtHllType1;
checkArrays(sk1bytes, sk2bytes, comb, false);
}
}
}
}
@Test
//Converts a type1 to a different type and converts back to type1 to check binary equivalence.
public void isomorphicCopyAsUpdatableHeap() {
for (int lgK = 4; lgK <= 21; lgK++) { //All LgK
for (int cm = 0; cm <= 2; cm++) { //List, Set, Hll
if ((lgK < 8) && (cm == 1)) { continue; } //lgk < 8 list transistions directly to HLL
CurMode curMode = CurMode.fromOrdinal(cm);
for (int t1 = 0; t1 <= 2; t1++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType1 = TgtHllType.fromOrdinal(t1);
HllSketch sk1 = buildHeapSketch(lgK, tgtHllType1, curMode);
byte[] sk1bytes = sk1.toUpdatableByteArray(); //UPDATABLE
for (int t2 = 0; t2 <= 2; t2++) { //HLL_4, HLL_6, HLL_8
if (t2 == t1) { continue; }
TgtHllType tgtHllType2 = TgtHllType.fromOrdinal(t2);
HllSketch sk2 = sk1.copyAs(tgtHllType2); //COPY AS
HllSketch sk1B = sk2.copyAs(tgtHllType1); //COPY AS
byte[] sk1Bbytes = sk1B.toUpdatableByteArray(); //UPDATABLE
String comb = "LgK= " + lgK + ", CurMode= " + curMode.toString()
+ ", Type1: " + tgtHllType1 + ", Type2: " + tgtHllType2;
checkArrays(sk1bytes, sk1Bbytes, comb, false);
}
}
}
}
}
@Test
//Converts a type1 to a different type and converts back to type1 to check binary equivalence.
public void isomorphicCopyAsCompactHeap() {
for (int lgK = 4; lgK <= 21; lgK++) { //All LgK
for (int cm = 0; cm <= 2; cm++) { //List, Set, Hll
if ((lgK < 8) && (cm == 1)) { continue; } //lgk < 8 list transistions directly to HLL
CurMode curMode = CurMode.fromOrdinal(cm);
for (int t1 = 0; t1 <= 2; t1++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType1 = TgtHllType.fromOrdinal(t1);
HllSketch sk1 = buildHeapSketch(lgK, tgtHllType1, curMode);
byte[] sk1bytes = sk1.toCompactByteArray(); //COMPACT
for (int t2 = 0; t2 <= 2; t2++) { //HLL_4, HLL_6, HLL_8
if (t2 == t1) { continue; }
TgtHllType tgtHllType2 = TgtHllType.fromOrdinal(t2);
HllSketch sk2 = sk1.copyAs(tgtHllType2); //COPY AS
HllSketch sk3 = sk2.copyAs(tgtHllType1); //COPY AS
byte[] sk3bytes = sk3.toCompactByteArray(); //COMPACT
String comb = "LgK= " + lgK + ", CurMode= " + curMode.toString()
+ ", Type1: " + tgtHllType1 + ", Type2: " + tgtHllType2;
checkArrays(sk1bytes, sk3bytes, comb, false);
}
}
}
}
}
@Test
//Compares two HLL to HLL merges. The input sketch varies by tgtHllType.
//The LgKs can be equal or the source sketch is one larger.
//The result of the union is converted to HLL_8 and checked between different combinations of
//heap, memory for binary equivalence.
public void isomorphicHllMerges() {
for (int uLgK = 4; uLgK <= 20; uLgK++) { //All LgK
int skLgK = uLgK;
for (int t1 = 0; t1 <= 2; t1++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType = TgtHllType.fromOrdinal(t1);
innerLoop(uLgK, skLgK, tgtHllType);
}
skLgK = uLgK + 1;
for (int t1 = 0; t1 <= 2; t1++) { //HLL_4, HLL_6, HLL_8
TgtHllType tgtHllType = TgtHllType.fromOrdinal(t1);
innerLoop(uLgK, skLgK, tgtHllType);
}
}
}
private static void innerLoop(int uLgK, int skLgK, TgtHllType tgtHllType) {
Union u;
HllSketch sk, skOut;
//CASE 1 Heap Union, Heap sketch
u = buildHeapUnionHllMode(uLgK, 0);
sk = buildHeapSketchHllMode(skLgK, tgtHllType, 1 << uLgK);
u.update(sk);
byte[] bytesOut1 = u.getResult(HLL_8).toUpdatableByteArray();
//CASE 2 Heap Union, Memory sketch
u = buildHeapUnionHllMode(uLgK, 0);
sk = buildMemorySketchHllMode(skLgK, tgtHllType, 1 << uLgK);
u.update(sk);
byte[] bytesOut2 = u.getResult(HLL_8).toUpdatableByteArray();
//println("Uheap/SkHeap HIP: " + bytesToDouble(bytesOut1, 8)); //HipAccum
//println("Uheap/SkMemory HIP: " + bytesToDouble(bytesOut2, 8)); //HipAccum
String comb = "uLgK: " + uLgK + ", skLgK: " + skLgK
+ ", SkType: " + tgtHllType.toString()
+ ", Case1: Heap Union, Heap sketch; Case2: /Heap Union, Memory sketch";
checkArrays(bytesOut1, bytesOut2, comb, false);
//CASE 3 Offheap Union, Heap sketch
u = buildMemoryUnionHllMode(uLgK, 0);
sk = buildHeapSketchHllMode(skLgK, tgtHllType, 1 << uLgK);
u.update(sk);
byte[] bytesOut3 = u.getResult(HLL_8).toUpdatableByteArray();
//println("Uheap/SkMemory HIP: " + bytesToDouble(bytesOut2, 8)); //HipAccum
//println("Umemory/SkHeap HIP: " + bytesToDouble(bytesOut3, 8)); //HipAccum
comb = "LgK: " + uLgK + ", skLgK: " + skLgK
+ ", SkType: " + tgtHllType.toString()
+ ", Case2: Heap Union, Memory sketch; Case3: /Memory Union, Heap sketch";
checkArrays(bytesOut2, bytesOut3, comb, false);
//Case 4 Memory Union, Memory sketch
u = buildMemoryUnionHllMode(uLgK, 0);
sk = buildMemorySketchHllMode(skLgK, tgtHllType, 1 << uLgK);
u.update(sk);
byte[] bytesOut4 = u.getResult(HLL_8).toUpdatableByteArray();
comb = "LgK: " + uLgK + ", skLgK: " + skLgK
+ ", SkType: " + tgtHllType.toString()
+ ", Case2: Heap Union, Memory sketch; Case4: /Memory Union, Memory sketch";
checkArrays(bytesOut2, bytesOut4, comb, false);
}
@Test
//Creates a binary reference: HLL_8 merged with union, HLL_8 result binary.
//Case 1: HLL_6 merged with a union, HLL_8 result binary compared with the reference.
//Case 2: HLL_4 merged with a union, Hll_8 result binary compared with the reference.
//Both Case 1 and 2 should differ in the binary output compared with the reference only for the
//HllAccum register.
public void isomorphicHllMerges2() {
byte[] bytesOut8, bytesOut6, bytesOut4;
String comb;
Union u;
HllSketch sk;
for (int lgK = 4; lgK <= 4; lgK++) { //All LgK
u = buildHeapUnionHllMode(lgK, 0);
sk = buildHeapSketchHllMode(lgK, HLL_8, 1 << lgK);
u.update(sk);
bytesOut8 = u.getResult(HLL_8).toUpdatableByteArray(); //The reference
u = buildHeapUnionHllMode(lgK, 0);
sk = buildHeapSketchHllMode(lgK, HLL_6, 1 << lgK);
u.update(sk);
bytesOut6 = u.getResult(HLL_8).toUpdatableByteArray();//should be identical except for HllAccum
comb = "LgK: " + lgK + ", SkType: HLL_6, Compared with SkType HLL_8";
checkArrays(bytesOut8, bytesOut6, comb, false);
u = buildHeapUnionHllMode(lgK, 0);
sk = buildHeapSketchHllMode(lgK, HLL_4, 1 << lgK);
u.update(sk);
bytesOut4 = u.getResult(HLL_8).toUpdatableByteArray();//should be identical except for HllAccum
comb = "LgK: " + lgK + ", SkType: HLL_4, Compared with SkType HLL_8";
checkArrays(bytesOut8, bytesOut4, comb, false);
}
}
private static void checkArrays(byte[] sk1, byte[] sk2, String comb, boolean omitHipAccum) {
int len = sk1.length;
if (len != sk2.length) {
println("Sketch images not the same length: " + comb);
return;
}
print(comb + ": ");
for (int i = 0; i < len; i++) {
if (omitHipAccum && (i >= 8) && (i <= 15)) { continue; }
if (sk1[i] == sk2[i]) { continue; }
print(i + " ");
fail();
}
println("");
}
//BUILDERS
private Union buildHeapUnion(int lgMaxK, CurMode curMode) {
Union u = new Union(lgMaxK);
int n = (curMode == null) ? 0 : getN(lgMaxK, curMode);
for (int i = 0; i < n; i++) { u.update(i + v); }
v += n;
return u;
}
private Union buildMemoryUnion(int lgMaxK, CurMode curMode) {
final int bytes = HllSketch.getMaxUpdatableSerializationBytes(lgMaxK, TgtHllType.HLL_8);
WritableMemory wmem = WritableMemory.allocate(bytes);
Union u = new Union(lgMaxK, wmem);
int n = (curMode == null) ? 0 : getN(lgMaxK, curMode);
for (int i = 0; i < n; i++) { u.update(i + v); }
v += n;
return u;
}
private HllSketch buildHeapSketch(int lgK, TgtHllType tgtHllType, CurMode curMode) {
HllSketch sk = new HllSketch(lgK, tgtHllType);
int n = (curMode == null) ? 0 : getN(lgK, curMode);
for (int i = 0; i < n; i++) { sk.update(i + v); }
v += n;
return sk;
}
private HllSketch buildMemorySketch(int lgK, TgtHllType tgtHllType, CurMode curMode) {
final int bytes = HllSketch.getMaxUpdatableSerializationBytes(lgK,tgtHllType);
WritableMemory wmem = WritableMemory.allocate(bytes);
HllSketch sk = new HllSketch(lgK, tgtHllType, wmem);
int n = (curMode == null) ? 0 : getN(lgK, curMode);
for (int i = 0; i < n; i++) { sk.update(i + v); }
v += n;
return sk;
}
private static Union buildHeapUnionHllMode(int lgMaxK, int startN) {
Union u = new Union(lgMaxK);
int n = getN(lgMaxK, HLL);
for (int i = 0; i < n; i++) { u.update(i + startN); }
return u;
}
private static Union buildMemoryUnionHllMode(int lgMaxK, int startN) {
final int bytes = HllSketch.getMaxUpdatableSerializationBytes(lgMaxK, TgtHllType.HLL_8);
WritableMemory wmem = WritableMemory.allocate(bytes);
Union u = new Union(lgMaxK, wmem);
int n = getN(lgMaxK, HLL);
for (int i = 0; i < n; i++) { u.update(i + startN); }
return u;
}
private static HllSketch buildHeapSketchHllMode(int lgK, TgtHllType tgtHllType, int startN) {
HllSketch sk = new HllSketch(lgK, tgtHllType);
int n = getN(lgK, HLL);
for (int i = 0; i < n; i++) { sk.update(i + startN); }
return sk;
}
private static HllSketch buildMemorySketchHllMode(int lgK, TgtHllType tgtHllType, int startN) {
final int bytes = HllSketch.getMaxUpdatableSerializationBytes(lgK,tgtHllType);
WritableMemory wmem = WritableMemory.allocate(bytes);
HllSketch sk = new HllSketch(lgK, tgtHllType, wmem);
int n = getN(lgK, HLL);
for (int i = 0; i < n; i++) { sk.update(i + startN); }
return sk;
}
//if lgK >= 8, curMode != SET!
private static int getN(int lgK, CurMode curMode) {
if (curMode == LIST) { return 4; }
if (curMode == SET) { return 1 << (lgK - 4); }
return ((lgK < 8) && (curMode == HLL)) ? (1 << lgK) : 1 << (lgK - 3);
}
@Test
public void checkCurMinConversion() {
TgtHllType hll8 = HLL_8;
TgtHllType hll4 = HLL_4;
for (int lgK = 4; lgK <= 21; lgK++) {
HllSketch sk8 = new HllSketch(lgK, hll8);
//The Coupon Collector Problem predicts that all slots will be filled by k Log(k).
int n = (1 << lgK) * lgK;
for (int i = 0; i < n; i++) { sk8.update(i); }
double est8 = sk8.getEstimate();
AbstractHllArray aharr8 = (AbstractHllArray)sk8.hllSketchImpl;
int curMin8 = aharr8.getCurMin();
int numAtCurMin8 = aharr8.getNumAtCurMin();
HllSketch sk4 = sk8.copyAs(hll4);
AbstractHllArray aharr4 = (AbstractHllArray)sk4.hllSketchImpl;
int curMin4 = ((AbstractHllArray)sk4.hllSketchImpl).getCurMin();
int numAtCurMin4 =aharr4.getNumAtCurMin();
double est4 = sk4.getEstimate();
assertEquals(est4, est8, 0.0);
assertEquals(curMin4, 1);
//println("Est 8 = " + est8 + ", CurMin = " + curMin8 + ", #CurMin + " + numAtCurMin8);
//println("Est 4 = " + est4 + ", CurMin = " + curMin4 + ", #CurMin + " + numAtCurMin4);
}
}
private static double bytesToDouble(byte[] arr, int offset) {
long v = 0;
for (int i = offset; i < (offset + 8); i++) {
v |= (arr[i] & 0XFFL) << (i * 8);
}
return Double.longBitsToDouble(v);
}
@Test
public void printlnTest() {
println("PRINTING: "+this.getClass().getName());
}
/**
* @param o value to print
*/
static void println(Object o) {
print(o.toString() + "\n");
}
/**
* @param o value to print
*/
static void print(Object o) {
//System.out.print(o.toString()); //disable here
}
/**
* @param fmt format
* @param args arguments
*/
static void printf(String fmt, Object...args) {
//System.out.printf(fmt, args); //disable here
}
}