blob: 088b4462cb62473035c2bc9ca5178f9ad48a75e4 [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.quantiles;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE_UNALIGNED;
import static org.apache.datasketches.common.Util.LS;
import static org.apache.datasketches.common.Util.ceilingPowerOf2;
import static org.apache.datasketches.quantilescommon.QuantileSearchCriteria.EXCLUSIVE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.apache.datasketches.common.SketchesArgumentException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class DirectUpdateDoublesSketchTest {
@BeforeMethod
public void setUp() {
QuantilesDoublesSketch.rand.setSeed(32749); // make sketches deterministic for testing
}
@Test
public void checkSmallMinMax () {
final int k = 32;
final int n = 8;
final UpdatableQuantilesDoublesSketch qs1 = buildDQS(k, n);
final UpdatableQuantilesDoublesSketch qs2 = buildDQS(k, n);
final UpdatableQuantilesDoublesSketch qs3 = buildDQS(k, n);
for (int i = n; i >= 1; i--) {
qs1.update(i);
qs2.update(10+i);
qs3.update(i);
}
assertEquals(qs1.getQuantile (0.0, EXCLUSIVE), 1.0);
assertEquals(qs1.getQuantile (0.5, EXCLUSIVE), 5.0);
assertEquals(qs1.getQuantile (1.0, EXCLUSIVE), 8.0);
assertEquals(qs2.getQuantile (0.0, EXCLUSIVE), 11.0);
assertEquals(qs2.getQuantile (0.5, EXCLUSIVE), 15.0);
assertEquals(qs2.getQuantile (1.0, EXCLUSIVE), 18.0);
assertEquals(qs3.getQuantile (0.0, EXCLUSIVE), 1.0);
assertEquals(qs3.getQuantile (0.5, EXCLUSIVE), 5.0);
assertEquals(qs3.getQuantile (1.0, EXCLUSIVE), 8.0);
final double[] queries = {0.0, 0.5, 1.0};
final double[] resultsA = qs1.getQuantiles(queries, EXCLUSIVE);
assertEquals(resultsA[0], 1.0);
assertEquals(resultsA[1], 5.0);
assertEquals(resultsA[2], 8.0);
final QuantilesDoublesUnion union1 = QuantilesDoublesUnion.heapify(qs1);
union1.union(qs2);
final QuantilesDoublesSketch result1 = union1.getResult();
final QuantilesDoublesUnion union2 = QuantilesDoublesUnion.heapify(qs2);
union2.union(qs3);
final QuantilesDoublesSketch result2 = union2.getResult();
final double[] resultsB = result1.getQuantiles(queries, EXCLUSIVE);
printResults(resultsB);
assertEquals(resultsB[0], 1.0);
assertEquals(resultsB[1], 11.0);
assertEquals(resultsB[2], 18.0);
final double[] resultsC = result2.getQuantiles(queries, EXCLUSIVE);
assertEquals(resultsC[0], 1.0);
assertEquals(resultsC[1], 11.0);
assertEquals(resultsC[2], 18.0);
}
static void printResults(final double[] results) {
println(results[0] + ", " + results[1] + ", " + results[2]);
}
@Test
public void wrapEmptyUpdateSketch() {
final UpdatableQuantilesDoublesSketch s1 = QuantilesDoublesSketch.builder().build();
final MemorySegment seg = MemorySegment.ofBuffer(ByteBuffer.wrap(s1.toByteArray()).order(ByteOrder.nativeOrder()));
final UpdatableQuantilesDoublesSketch s2 = DirectUpdateDoublesSketch.wrapInstance(seg, null);
assertTrue(s2.isEmpty());
assertEquals(s2.getN(), 0);
assertTrue(Double.isNaN(s2.isEmpty() ? Double.NaN : s2.getMinItem()));
assertTrue(Double.isNaN(s2.isEmpty() ? Double.NaN : s2.getMaxItem()));
s2.reset(); // empty: a no-op
assertEquals(s2.getN(), 0);
}
@Test
public void checkPutCombinedBuffer() {
final int k = PreambleUtil.DEFAULT_K;
final int cap = 32 + ((2 * k) << 3);
MemorySegment seg = MemorySegment.ofArray(new byte[cap]);
final UpdatableQuantilesDoublesSketch qs = QuantilesDoublesSketch.builder().setK(k).build(seg);
seg = qs.getMemorySegment();
assertEquals(seg.byteSize(), cap);
assertTrue(qs.isEmpty());
final int n = 16;
final double[] data = new double[n];
for (int i = 0; i < n; ++i) {
data[i] = i + 1;
}
qs.putBaseBufferCount(n);
qs.putN(n);
qs.putCombinedBuffer(data);
final double[] combBuf = qs.getCombinedBuffer();
assertEquals(combBuf, data);
// shouldn't have changed min/max values
assertTrue(Double.isNaN(qs.getMinItem()));
assertTrue(Double.isNaN(qs.getMaxItem()));
}
@Test
public void checkMisc() {
final int k = PreambleUtil.DEFAULT_K;
final int n = 48;
final int cap = 32 + ((2 * k) << 3);
MemorySegment seg = MemorySegment.ofArray(new byte[cap]);
UpdatableQuantilesDoublesSketch qs = QuantilesDoublesSketch.builder().setK(k).build(seg);
seg = qs.getMemorySegment();
assertEquals(seg.byteSize(), cap);
double[] combBuf = qs.getCombinedBuffer();
assertEquals(combBuf.length, 2 * k);
qs = buildAndLoadDQS(k, n);
qs.update(Double.NaN);
final int n2 = (int)qs.getN();
assertEquals(n2, n);
combBuf = qs.getCombinedBuffer();
assertEquals(combBuf.length, ceilingPowerOf2(n)); // since n < k
println(qs.toString(true, true));
qs.reset();
assertEquals(qs.getN(), 0);
qs.putBaseBufferCount(0);
}
@SuppressWarnings("unused")
@Test
public void variousExceptions() {
final MemorySegment seg = MemorySegment.ofArray(new byte[8]);
try {
final int flags = PreambleUtil.COMPACT_FLAG_MASK;
DirectUpdateDoublesSketch.checkCompact(2, 0);
fail();
} catch (final SketchesArgumentException e) {} //OK
try {
final int flags = PreambleUtil.COMPACT_FLAG_MASK;
DirectUpdateDoublesSketch.checkCompact(3, flags);
fail();
} catch (final SketchesArgumentException e) {} //OK
try {
DirectUpdateDoublesSketch.checkPreLongs(3);
fail();
} catch (final SketchesArgumentException e) {} //OK
try {
DirectUpdateDoublesSketch.checkPreLongs(0);
fail();
} catch (final SketchesArgumentException e) {} //OK
try {
DirectUpdateDoublesSketch.checkDirectFlags(PreambleUtil.COMPACT_FLAG_MASK);
fail();
} catch (final SketchesArgumentException e) {} //OK
try {
DirectUpdateDoublesSketch.checkEmptyAndN(true, 1);
fail();
} catch (final SketchesArgumentException e) {} //OK
}
@Test
public void checkCheckDirectSegCapacity() {
final int k = 128;
DirectUpdateDoublesSketch.checkDirectSegCapacity(k, (2 * k) - 1, (4 + (2 * k)) * 8);
DirectUpdateDoublesSketch.checkDirectSegCapacity(k, (2 * k) + 1, (4 + (3 * k)) * 8);
DirectUpdateDoublesSketch.checkDirectSegCapacity(k, 0, 8);
try {
DirectUpdateDoublesSketch.checkDirectSegCapacity(k, 10000, 64);
fail();
} catch (final SketchesArgumentException e) {
// expected
}
}
@Test
public void serializeDeserialize() {
final int sizeBytes = QuantilesDoublesSketch.getUpdatableStorageBytes(128, 2000);
final MemorySegment seg = MemorySegment.ofArray(new byte[sizeBytes]);
final UpdatableQuantilesDoublesSketch sketch1 = QuantilesDoublesSketch.builder().build(seg);
for (int i = 0; i < 1000; i++) {
sketch1.update(i);
}
final UpdatableQuantilesDoublesSketch sketch2 = UpdatableQuantilesDoublesSketch.wrap(seg, null);
for (int i = 0; i < 1000; i++) {
sketch2.update(i + 1000);
}
assertEquals(sketch2.getMinItem(), 0.0);
assertEquals(sketch2.getMaxItem(), 1999.0);
assertEquals(sketch2.getQuantile(0.5), 1000.0, 10.0);
final byte[] arr2 = sketch2.toByteArray(false);
assertEquals(arr2.length, sketch2.getSerializedSizeBytes());
final QuantilesDoublesSketch sketch3 = QuantilesDoublesSketch.writableWrap(MemorySegment.ofArray(arr2), null);
assertEquals(sketch3.getMinItem(), 0.0);
assertEquals(sketch3.getMaxItem(), 1999.0);
assertEquals(sketch3.getQuantile(0.5), 1000.0, 10.0);
}
@Test
public void mergeTest() {
final QuantilesDoublesSketch dqs1 = buildAndLoadDQS(128, 256);
final QuantilesDoublesSketch dqs2 = buildAndLoadDQS(128, 256, 256);
final QuantilesDoublesUnion union = QuantilesDoublesUnion.builder().setMaxK(128).build();
union.union(dqs1);
union.union(dqs2);
final QuantilesDoublesSketch result = union.getResult();
final double median = result.getQuantile(0.5);
println("Median: " + median);
assertEquals(median, 258.0, .05 * 258);
}
@Test
public void checkSimplePropagateCarryDirect() {
final int k = 16;
final int n = k * 2;
final int segBytes = QuantilesDoublesSketch.getUpdatableStorageBytes(k, n);
final MemorySegment seg = MemorySegment.ofArray(new byte[segBytes]);
final QuantilesDoublesSketchBuilder bldr = QuantilesDoublesSketch.builder();
final UpdatableQuantilesDoublesSketch ds = bldr.setK(k).build(seg);
for (int i = 1; i <= n; i++) { // 1 ... n
ds.update(i);
}
double last = 0.0;
for (int i = 0; i < k; i++) { //check the level 0
final double d = seg.get(JAVA_DOUBLE_UNALIGNED, (4 + (2 * k) + i) << 3);
assertTrue(d > 0);
assertTrue(d > last);
last = d;
}
//println(ds.toString(true, true));
}
@Test
public void getRankAndGetCdfConsistency() {
final int k = 128;
final int n = 1_000_000;
final int segBytes = QuantilesDoublesSketch.getUpdatableStorageBytes(k, n);
final MemorySegment seg = MemorySegment.ofArray(new byte[segBytes]);
final UpdatableQuantilesDoublesSketch sketch = QuantilesDoublesSketch.builder().build(seg);
final double[] values = new double[n];
for (int i = 0; i < n; i++) {
sketch.update(i);
values[i] = i;
}
final double[] ranks = sketch.getCDF(values);
for (int i = 0; i < n; i++) {
assertEquals(ranks[i], sketch.getRank(values[i]));
}
}
static UpdatableQuantilesDoublesSketch buildAndLoadDQS(final int k, final int n) {
return buildAndLoadDQS(k, n, 0);
}
static UpdatableQuantilesDoublesSketch buildAndLoadDQS(final int k, final long n, final int startV) {
final UpdatableQuantilesDoublesSketch qs = buildDQS(k, n);
for (long i = 1; i <= n; i++) {
qs.update(startV + i);
}
return qs;
}
static UpdatableQuantilesDoublesSketch buildDQS(final int k, final long n) {
int cap = QuantilesDoublesSketch.getUpdatableStorageBytes(k, n);
if (cap < (2 * k)) { cap = 2 * k; }
final QuantilesDoublesSketchBuilder bldr = new QuantilesDoublesSketchBuilder();
bldr.setK(k);
return bldr.build(MemorySegment.ofArray(new byte[cap]));
}
@Test
public void printlnTest() {
println("PRINTING: " + this.getClass().getName());
}
/**
* @param s value to print
*/
static void println(final String s) {
print(s+LS);
}
/**
* @param s value to print
*/
static void print(final String s) {
//System.err.print(s); //disable here
}
}