From cb4553eb40a266457029abe142aeeed20d5a6bf5 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 18 Feb 2025 17:56:38 -0600 Subject: [PATCH] more function in and out types --- .../to_long/IntervalHistribution.java | 78 +++++++++++ .../to_long/UnitHistribution.java} | 38 +++--- .../shared/from_long/to_double/HashMix.java | 6 +- .../shared/from_long/to_long/HashMix.java | 122 ++++++++++++++++++ .../from_long/to_double/HashMixTest.java | 53 ++++++-- ...ionTest.java => UnitHistributionTest.java} | 13 +- 6 files changed, 272 insertions(+), 38 deletions(-) create mode 100644 nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/IntervalHistribution.java rename nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/{from_long/to_double/EmpiricalHistribution.java => from_double/to_long/UnitHistribution.java} (67%) create mode 100644 nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/HashMix.java rename nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/{EmpiricalHistributionTest.java => UnitHistributionTest.java} (86%) diff --git a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/IntervalHistribution.java b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/IntervalHistribution.java new file mode 100644 index 000000000..6f34f8680 --- /dev/null +++ b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/IntervalHistribution.java @@ -0,0 +1,78 @@ +package io.nosqlbench.virtdata.library.basics.shared.from_double.to_long; + +/* + * Copyright (c) nosqlbench + * + * Licensed 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. + */ + + +import io.nosqlbench.virtdata.api.annotations.Categories; +import io.nosqlbench.virtdata.api.annotations.Category; +import io.nosqlbench.virtdata.api.annotations.Example; +import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; +import io.nosqlbench.virtdata.api.bindings.VirtDataFunctions; +import io.nosqlbench.virtdata.library.basics.core.stathelpers.AliasSamplerDoubleLong; +import io.nosqlbench.virtdata.library.basics.core.stathelpers.EvProbLongDouble; +import io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.EmpiricalDistribution; +import io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.HashRange; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.DoubleToLongFunction; +import java.util.function.LongToDoubleFunction; +import java.util.function.LongUnaryOperator; + +@ThreadSafeMapper +@Categories(Category.distributions) +public class IntervalHistribution implements LongUnaryOperator { + + private final UnitHistribution sampler; + private final LongToDoubleFunction samplePointF; + + @Example({"IntervalHistribution('50 25 13 12')", "implied frequencies of 0:50 1:25 2:13 3:12"}) + @Example({ + "IntervalHistribution('234:50 33:25 17:13 3:12')", + "labeled frequencies; 234,33,17,3 are labels, and 50,25,13,12 are weights" + }) + public IntervalHistribution(String freqs, Object samplePointFunc) { + this.sampler = new UnitHistribution(freqs); + this.samplePointF = VirtDataFunctions.adapt( + samplePointFunc, + LongToDoubleFunction.class, + double.class, + false + ); + } + + public IntervalHistribution(String freqs) { + this(freqs,new HashRange(0.0d,1.0d)); + } + + + + private static List genEvents(long[] freqs) { + ArrayList events = new ArrayList<>(); + for (int i = 0; i < freqs.length; i++) { + events.add(new EvProbLongDouble(i, freqs[i])); + } + return events; + } + + @Override + public long applyAsLong(long operand) { + double samplePoint = this.samplePointF.applyAsDouble(operand); + return this.sampler.applyAsLong(samplePoint); + } +} diff --git a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistribution.java b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/UnitHistribution.java similarity index 67% rename from nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistribution.java rename to nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/UnitHistribution.java index ffee03a4e..8fe50ac5b 100644 --- a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistribution.java +++ b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_double/to_long/UnitHistribution.java @@ -1,4 +1,4 @@ -package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; +package io.nosqlbench.virtdata.library.basics.shared.from_double.to_long; /* * Copyright (c) nosqlbench @@ -18,16 +18,17 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; */ -import io.nosqlbench.nb.api.errors.BasicError; import io.nosqlbench.virtdata.api.annotations.Categories; import io.nosqlbench.virtdata.api.annotations.Category; import io.nosqlbench.virtdata.api.annotations.Example; import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; import io.nosqlbench.virtdata.library.basics.core.stathelpers.AliasSamplerDoubleLong; import io.nosqlbench.virtdata.library.basics.core.stathelpers.EvProbLongDouble; +import io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.EmpiricalDistribution; import java.util.ArrayList; import java.util.List; +import java.util.function.DoubleToLongFunction; /// Empirical Histribution is a portmanteau name to capture the /// concept of an empirical distribution based on a discrete histogram. @@ -38,14 +39,14 @@ import java.util.List; /// want to represent accurately. @ThreadSafeMapper @Categories(Category.distributions) -public class EmpiricalHistribution extends AliasSamplerDoubleLong { +public class UnitHistribution extends AliasSamplerDoubleLong implements DoubleToLongFunction { - @Example({"EmpiricalHistribution('50 25 13 12')", "implied frequencies of 0:50 1:25 2:13 3:12"}) + @Example({"UnitHistribution('50 25 13 12')", "implied frequencies of 0:50 1:25 2:13 3:12"}) @Example({ - "EmpiricalHistribution('234:50 33:25 17:13 3:12')", + "UnitHistribution('234:50 33:25 17:13 3:12')", "labeled frequencies; 234,33,17,3 are labels, and 50,25,13,12 are weights" }) - public EmpiricalHistribution(String freqs) { + public UnitHistribution(String freqs) { List events = new ArrayList<>(); boolean labeled = (freqs.contains(":")); @@ -57,20 +58,21 @@ public class EmpiricalHistribution extends AliasSamplerDoubleLong { "If any elements are labeled, all elements must be:" + freqs); } long id = labeled ? Long.parseLong(parts[0]) : i; - events.add(new EvProbLongDouble(id, Long.parseLong(parts[1]))); + long weight = Long.parseLong(parts[labeled ? 1 : 0]); + events.add(new EvProbLongDouble(id, weight)); } super(events); } - public EmpiricalHistribution(long... freqs) { - super(genEvents(freqs)); - } - - private static List genEvents(long[] freqs) { - ArrayList events = new ArrayList<>(); - for (int i = 0; i < freqs.length; i++) { - events.add(new EvProbLongDouble(i, freqs[i])); - } - return events; - } + // public UnitHistribution(long... freqs) { + // super(genEvents(freqs)); + // } + // + // private static List genEvents(long[] freqs) { + // ArrayList events = new ArrayList<>(); + // for (int i = 0; i < freqs.length; i++) { + // events.add(new EvProbLongDouble(i, freqs[i])); + // } + // return events; + // } } diff --git a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMix.java b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMix.java index 6d15eeebe..71dd09ab7 100644 --- a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMix.java +++ b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMix.java @@ -2,13 +2,13 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; /* * Copyright (c) nosqlbench - * + * * Licensed 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 diff --git a/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/HashMix.java b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/HashMix.java new file mode 100644 index 000000000..16bf1d642 --- /dev/null +++ b/nb-virtdata/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/HashMix.java @@ -0,0 +1,122 @@ +package io.nosqlbench.virtdata.library.basics.shared.from_long.to_long; + +/* + * Copyright (c) nosqlbench + * + * Licensed 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. + */ + + +import io.nosqlbench.virtdata.api.annotations.Categories; +import io.nosqlbench.virtdata.api.annotations.Category; +import io.nosqlbench.virtdata.api.annotations.Example; +import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; + +import java.util.function.LongToDoubleFunction; +import java.util.function.LongUnaryOperator; + +/// Blends two functions with a domain of 0..Long.MAX_VALUE as the input interval, +/// and a double output. The output value is interpolated between the output value +/// of the two according to the mix function. When the mix function yields a value +/// of 0.0, then the mix is turned _fully counter-clockwise_., or fully on the first provided +/// function. When the value is 1.0, the mix is turned all the clockwise, or fully on the second +/// provided function. +/// +/// If there are only two inner functions provided to HashMix, then it will default to +/// sampling random mixes at a randomized sample point. In other words, the variates +/// provided will be somewhere between the two curves on the unit interval. This is a simple way +/// to sample between two curves by default. The yielded value will be greater than or equal to +/// the lower of the two values at any point, and less than or equal to the greater of either. +/// +/// If a third parameter is provided to control the mix, then the mix can be set directly as a +/// unit interval. (The dial goes from 0.0 to 1.0). Any double or float value here will suffice. +/// You can use this when you want to have a test parameter that slews between two modeled +/// shapes. You can alternately provide any other function which can be coerced to a LongToDouble +/// function as a dynamic mix control. IFF such a function is provided, it must also be responsible +/// for hashing the input value if pseudo-randomness is desired. +/// +/// If a fourth parameter is provided, the sample point can also be controlled. By default, the +/// values on the provided curves will be sampled pseudo-randomly. However, a fourth parameter +/// can override this just like the mix ratio. As well, if you provide a value or function +/// to control the sample point, you are also responsible for any hashing needed to sample across +/// the whole space of possible values. +/// +/// The flexibility of these two parameters provides a substantial amount of flexibility. You +/// can, for example: +/// +/// - sample variates between two curves +/// - sample variates at a selected morphing step between the curves +/// - sample variates between two curves on a subsection of the unit interval +/// - sample variates within a defined band gap of the two curves +@ThreadSafeMapper +@Categories(Category.functional) +public class HashMix implements LongUnaryOperator { + + private io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.HashMix mixer; + + + @Example({ + "IntervalHashMix(Func1(),Func2())", + "yield samples between func1 and func2 values at some random random sample point x" + }) + @Example({ + "IntervalHashMix(Func1(),Func2(),0.25d)", + "yield samples which are 25% from the sample values for func1 and func2 at some random " + + "sample point x" + }) + @Example({ + "IntervalHashMix(Func1(),Func2(),HashRange(0.25d,0.75d)", + "yield samples between 25% and 75% from func1 to func2 values at some random sample point x" + }) + @Example({ + "IntervalHashMix(Func1(),Func2(),0.0d,ScaledDouble())", + "access Func1 values as if it were the only one provided. ScaledDouble adds no " + + "randomization the input value, but it does map it to the sample domain of 0.0d-0.1d." + }) + public HashMix(Object curve1F, Object curve2F, Object mixPointF, Object samplePointF) { + this.mixer = new io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.HashMix(curve1F, curve2F, mixPointF, samplePointF); + } + + public HashMix(Object curve1F, Object curve2F, Object mixPointF) { + this( + curve1F, + curve2F, + mixPointF, + new HashRange(Long.MAX_VALUE) + ); + } + + public HashMix(Object curve1F, Object curve2F) { + this( + curve1F, + curve2F, + new io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.HashRange(0.0d, 1.0d), + new HashRange(Long.MAX_VALUE) + ); + } + + public HashMix(LongToDoubleFunction f1, LongToDoubleFunction f2) { + this( + f1, + f2, + new io.nosqlbench.virtdata.library.basics.shared.from_long.to_double.HashRange(0.0d, 1.0d), + new HashRange(Long.MAX_VALUE) + ); + } + + @Override + public long applyAsLong(long value) { + return (long) mixer.applyAsDouble(value); + } +} diff --git a/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMixTest.java b/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMixTest.java index c9df86b4a..a197e0012 100644 --- a/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMixTest.java +++ b/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/HashMixTest.java @@ -2,13 +2,13 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; /* * Copyright (c) nosqlbench - * + * * Licensed 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 @@ -22,6 +22,7 @@ import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; import java.util.function.LongToDoubleFunction; +import java.util.function.LongUnaryOperator; import static org.assertj.core.api.Assertions.assertThat; @@ -33,12 +34,16 @@ public class HashMixTest { @Test public void testLinearMix() { - HashMix um1 = new HashMix(TO_UNIT_INTERVAL, TO_UNIT_INTERVAL); - for (long i = 1; i < (Long.MAX_VALUE >> 1); i *= 2) { - assertThat(um1.applyAsDouble(i)).isEqualTo( - TO_UNIT_INTERVAL.applyAsDouble(i), - Offset.offset(0.0000001d) - ); + DoubleHolder dh = new DoubleHolder(); + LongHolder lh = new LongHolder(); + HashMix um1 = new HashMix(TO_UNIT_INTERVAL, TO_UNIT_INTERVAL,dh, lh); + for (long i = 0; i >= 0L; i += 1L << 58) { + double fraction = TO_UNIT_INTERVAL.applyAsDouble(i); + lh.setValue(i); + dh.setValue(fraction); + double actual = um1.applyAsDouble(i); + double expected = TO_UNIT_INTERVAL.applyAsDouble(i); + assertThat(actual).isEqualTo(expected, Offset.offset(0.0000001d)); } } @@ -46,11 +51,37 @@ public class HashMixTest { public void testCrossfadeMix() { LongToDoubleFunction rampdown1 = l -> 1.0d - TO_UNIT_INTERVAL.applyAsDouble(l); LongToDoubleFunction rampdown2 = l -> 2.0d - TO_UNIT_INTERVAL.applyAsDouble(l); - HashMix um1 = new HashMix(rampdown1,rampdown2); - for (long i = 1<<24; i <= Long.MAX_VALUE>>1; i<<=1) { + HashMix um1 = new HashMix(rampdown1, rampdown2); + for (long i = 0; i >= 0L; i += 1L << 58) { double value = um1.applyAsDouble(i); assertThat(um1.applyAsDouble(i)).isEqualTo(1.0d, Offset.offset(0.0000001d)); } } + private class DoubleHolder implements LongToDoubleFunction { + + private double value; + + @Override + public double applyAsDouble(long value) { + return this.value; + } + + public void setValue(double value) { + this.value = value; + } + } + + private class LongHolder implements LongUnaryOperator { + private long value; + + @Override + public long applyAsLong(long operand) { + return this.value; + } + + public void setValue(long value) { + this.value = value; + } + } } diff --git a/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistributionTest.java b/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/UnitHistributionTest.java similarity index 86% rename from nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistributionTest.java rename to nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/UnitHistributionTest.java index 90bad9d9c..d709ce5d9 100644 --- a/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/EmpiricalHistributionTest.java +++ b/nb-virtdata/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_double/UnitHistributionTest.java @@ -2,13 +2,13 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; /* * Copyright (c) nosqlbench - * + * * Licensed 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 @@ -18,6 +18,7 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double; */ +import io.nosqlbench.virtdata.library.basics.shared.from_double.to_long.UnitHistribution; import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; @@ -26,17 +27,17 @@ import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class EmpiricalHistributionTest { +public class UnitHistributionTest { @Test public void testUniformSyntaxRequired() { - assertThatThrownBy(() -> new EmpiricalHistribution("1 2:2 3:3")).hasMessageContaining( + assertThatThrownBy(() -> new UnitHistribution("1 2:2 3:3")).hasMessageContaining( "all elements must be"); } @Test public void testBasicHistribution() { - EmpiricalHistribution h = new EmpiricalHistribution("1:1 2:2 3:3"); + UnitHistribution h = new UnitHistribution("1:1 2:2 3:3"); long[] counts = new long[10]; int total=1000000; HashRange hr = new HashRange(0.0d, 1.0d);