partial functions work

This commit is contained in:
Jonathan Shook
2023-05-30 17:41:01 -05:00
committed by jeffbanks
parent b324c59c11
commit 56b96ec1aa
9 changed files with 330 additions and 110 deletions

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.api.bindings.VirtDataConversions;
import java.util.function.LongFunction;
/**
* Wrap any function producing a valid numeric value as a float.
*/
@Categories(Category.conversion)
@ThreadSafeMapper
public class ToFloat implements LongFunction<Float> {
private final LongFunction<Float> func;
ToFloat(Object funcOrValue) {
if (funcOrValue instanceof Number number) {
final float afloat = number.floatValue();
this.func = l -> afloat;
} else {
this.func = VirtDataConversions.adaptFunction(funcOrValue,LongFunction.class,Float.class);
}
}
@Override
public Float apply(long value) {
return func.apply(value);
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright (c) 2022 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_double;
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;
/**
* Return a value along an interpolation curve. This allows you to sketch a basic
* density curve and describe it simply with just a few values. The number of values
* provided determines the resolution of the internal lookup table that is used for
* interpolation. The first value is always the 0.0 anchoring point on the unit interval.
* The last value is always the 1.0 anchoring point on the unit interval. This means
* that in order to subdivide the density curve in an interesting way, you need to provide
* a few more values in between them. Providing two values simply provides a uniform
* sample between a minimum and maximum value.
*
* The input range of this function is, as many of the other functions in this library,
* based on the valid range of positive long values, between 0L and Long.MAX_VALUE inclusive.
* This means that if you want to combine interpolation on this curve with the effect of
* pseudo-random sampling, you need to put a hash function ahead of it in the flow.
*
* Developer Note: This is the canonical implementation of LERPing in NoSQLBench, so is
* heavily documented. Any other LERP implementations should borrow directly from this,
* embedding by default.
*/
@ThreadSafeMapper
@Categories({Category.general})
public class Interpolate implements LongToDoubleFunction {
// How many values we have to pick from
private final double resolution;
// The lookup table
private final double[] lut;
/**
* The scale of Long.MAX_VALUE and the unit interval scale factor are pre-combined
* here to reduce the number of operations later.
*
* The LUT size is retained as the number of elements provided (resolution) + 1.
* The +1 element serves as the N+1 index for when the unit interval sample is
* 1.0. In other words, the maximum value is not a special case, as a duplicate
* value is appended to the LUT instead.
*
* This size is the scale factor from the unit interval to the array index. Since
* the input comes in as a long value, it is mapped from [0L, Long.MAX_VALUE] to
* [0.0D, 1.0D] by multiplying by (1.0/(double)Long.MAX_VALUE). The long input
* value can then be multiplied directly to yield a double in the range of
* [0,LUT.length-1], which simplifies all remaining LERP math.
*
*/
private final double scaleToLongInterval;
@Example({"Interpolate(0.0d,100.0d)", "return a uniform double value between 0.0d and 100.0d"})
@Example({"Interpolate(0.0d,90.0d,95.0d,98.0d,100.0d)", "return a weighted double value where the first second and third quartiles are 90.0D, 95.0D, and 98.0D"})
public Interpolate(double... values) {
this.resolution = values.length;
double[] doubles = new double[values.length + 1];
System.arraycopy(values,0,doubles,0,values.length);
doubles[doubles.length - 1] = doubles[doubles.length - 2];
this.lut = doubles;
this.scaleToLongInterval = (this.resolution - 1) * (1.0d / (double) Long.MAX_VALUE);
}
@Override
public double applyAsDouble(long input) {
// scale the input from [0,Long.MAX_VALUE] to [0.0,lut.length-1]
double samplePoint = scaleToLongInterval * input;
// truncate the sample point to the left index
int leftidx = (int) samplePoint;
// isolate the fractional component
double fractional = samplePoint - leftidx;
// take the sum of the left component and right component
// scaled by closeness to fractional point within the interval, respectively
double sample = (lut[leftidx] * (1.0d - fractional)) + (lut[leftidx + 1] * fractional);
return sample;
}
}

View File

@@ -20,19 +20,19 @@ import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import java.util.function.LongToDoubleFunction;
import java.util.function.LongFunction;
@ThreadSafeMapper
@Categories({Category.general})
public class Mul implements LongToDoubleFunction {
private final double factor;
public class Mul implements LongFunction<Float> {
private final float factor;
public Mul(double factor) {
public Mul(float factor) {
this.factor = factor;
}
@Override
public double applyAsDouble(long value) {
public Float apply(long value) {
return factor * value;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2022 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_float;
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.LongFunction;
@ThreadSafeMapper
@Categories({Category.general})
public class FixedValues implements LongFunction<Float> {
private final float[] fixedValues;
@Example({"FixedValues(3.0,53.0,73d)", "Yield 3D, 53D, 73D, 3D, 53D, 73D, 3D, ..."})
public FixedValues(Object... values) {
this.fixedValues = new float[values.length];
for (int i = 0; i < values.length; i++) {
Object value = values[i];
if (value instanceof Number number) {
fixedValues[i]=number.floatValue();
} else {
throw new RuntimeException("Not a number: " + value);
}
}
}
@Override
public Float apply(long value) {
int index = (int) (value % Integer.MAX_VALUE) % fixedValues.length;
return fixedValues[index];
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_float;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.Hash;
import java.util.function.LongFunction;
/*
* <p>This simulates a uniform sample from a range of double values
* via long hashing. This function attempts to take a double
* unit interval value from a long/long division over the whole
* range of long values but via double value types, thus providing
* a very linear sample. This means that the range of double
* values to be accessed will not fall along all possible doubles,
* but will still provide suitable values for ranges close to
* high-precision points in the IEEE floating point number line.
* This suffices for most reasonable ranges in practice outside
* of scientific computing, where large exponents put adjacent
* IEEE floating point values much further apart.</p>
*
* <p>This should be consider the default double range sampling
* function for most uses, when the exponent is not needed for
* readability.</p>
*/
/**
* Return a double value within the specified range. This function
* uses an intermediate long to arrive at the sampled value before
* conversion to double, thus providing a more linear sample at the
* expense of some precision at extremely large values.
*/
@ThreadSafeMapper
@Categories({Category.general})
public class HashedFloatRange implements LongFunction<Float> {
private final float min;
private final float max;
private final float interval;
private final static float MAX_FLOAT_VIA_LONG = (float) Long.MAX_VALUE;
private final Hash hash = new Hash();
public HashedFloatRange(float min, float max) {
this.min = min;
this.max = max;
this.interval = max - min;
}
@Override
public Float apply(long value) {
long hashed = hash.applyAsLong(value);
float unitScale = ((float) hashed) / MAX_FLOAT_VIA_LONG;
float valueScaled =interval*unitScale + min;
return valueScaled;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_float;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.Hash;
import java.util.function.LongFunction;
/**
* This provides a random sample of a double in a range, without
* accounting for the non-uniform distribution of IEEE double representation.
* This means that values closer to high-precision areas of the IEEE spec
* will be weighted higher in the output. However, NaN and positive and
* negative infinity are filtered out via oversampling. Results are still
* stable for a given input value.
*/
@ThreadSafeMapper
@Categories({Category.general})
public class HashedRangedToNonuniformFloat implements LongFunction<Float> {
private final long min;
private final long max;
private final float length;
private final Hash hash;
public HashedRangedToNonuniformFloat(long min, long max) {
this.hash = new Hash();
if (max<=min) {
throw new RuntimeException("max must be >= min");
}
this.min = min;
this.max = max;
this.length = (float) max - min;
}
public String toString() {
return getClass().getSimpleName() + ":" + min + ":" + max;
}
@Override
public Float apply(long input) {
long bitImage = hash.applyAsLong(input);
double value = Math.abs(Double.longBitsToDouble(bitImage));
while (!Double.isFinite(value)) {
input++;
bitImage = hash.applyAsLong(input);
value = Math.abs(Double.longBitsToDouble(bitImage));
}
value %= length;
value += min;
float floatValue = (float) value;
return floatValue;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2022-2023 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_float;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import java.util.function.LongToDoubleFunction;
@ThreadSafeMapper
@Categories({Category.general})
public class Mul implements LongToDoubleFunction {
private final double factor;
public Mul(double factor) {
this.factor = factor;
}
@Override
public double applyAsDouble(long value) {
return factor * value;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2022 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.
*/
package io.nosqlbench.virtdata.library.basics.shared.from_long.to_float;
import io.nosqlbench.virtdata.api.annotations.Categories;
import io.nosqlbench.virtdata.api.annotations.Category;
import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper;
import java.util.function.LongToDoubleFunction;
/**
* Convert the input value to a double.
*/
@ThreadSafeMapper
@Categories({Category.conversion})
public class ToDouble implements LongToDoubleFunction {
@Override
public double applyAsDouble(long value) {
return (double) value;
}
}