All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.meliorbis.numerics.generic.primitives.impl.DoubleArrayFunctions Maven / Gradle / Ivy

Go to download

A library for working with large multi-dimensional arrays and the functions they represent

There is a newer version: 1.2
Show newest version
	/**
 *
 */
package com.meliorbis.numerics.generic.primitives.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;

import com.meliorbis.numerics.DoubleNumerics;
import com.meliorbis.numerics.Numerics;
import com.meliorbis.numerics.NumericsException;
import com.meliorbis.numerics.generic.MultiDimensionalArray;
import com.meliorbis.numerics.generic.MultiDimensionalArrayException;
import com.meliorbis.numerics.generic.primitives.DoubleArray;
import com.meliorbis.numerics.generic.primitives.DoubleIndexedReduction;
import com.meliorbis.numerics.generic.primitives.DoubleNaryOp;
import com.meliorbis.numerics.generic.primitives.DoubleReduction;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIndexedIterator;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIterator;
import com.meliorbis.numerics.generic.primitives.DoubleSubspaceSplitIterator;
import com.meliorbis.numerics.generic.primitives.DoubleUnaryOp;
import com.meliorbis.utils.Pair;
import com.meliorbis.utils.Utils;

/**
 * Some functions that could be in the array class but are too much of a pain to
 * implement generically
 *
 * @author Tobias Grasl
 */
public final class DoubleArrayFunctions
{
	private static DoubleBlockedArrayFactory _arrayFactory;
	private static Numerics _numerics;

	static
	{
		_numerics = DoubleNumerics.instance();
		_arrayFactory = _numerics.getArrayFactory();
	}
	
	private DoubleArrayFunctions()
	{
	}

	public static DoubleArray diff(final DoubleArray Y_, int dimension_)
	{

		// Create an identically sizes array as this to hold the result
		final DoubleArray result = _arrayFactory.newArray(Y_.size());

		Y_.across(dimension_).reduce((DoubleIndexedReduction) iterator_ -> {

			DoubleSubspaceSplitIterator subSpaceIterator = (DoubleSubspaceSplitIterator) iterator_;

			double y1 = subSpaceIterator.nextDouble();

			int[] priorIndex = subSpaceIterator.getFullIndex();

			while (subSpaceIterator.hasNext())
			{
				double y2 = subSpaceIterator.nextDouble();

				double diff = y2 - y1;

				result.set(diff, priorIndex);

				priorIndex = subSpaceIterator.getFullIndex();

				y1 = y2;
			}

			// Need to set the final one too!
				result.set(0d, priorIndex);

				return 0d;
			});

		return result;
	}

	/**
	 * Calculates the gradient of the grid along the given dimension, assuming
	 * that the points on the grid are y-values and the points given in points_
	 * are x-values.
	 * 

* The grad is calculated as the weighted mean of the upper and lower * gradients except at the points on the boundary, where it is only the * upper or lower as appropriate. The weighting as in Fritsch & Carlson, * 1980. The gradient is the difference between two successive points * divided by the difference between the corresponding x values. * * @param Y_ * The function values * @param X_ * The x-values assumed at each grid point in the given * dimension. If X_ is one dimensional, they are constant across * other dimensions; if not, then X_ must be the same size as Y_ * and each point can have a value for X_. * @param dimension_ * The dimension along the specified dimension * @return An array of the same dimension as this one with the gradient at * each point */ public static DoubleArray gradWeigthedHarmonic(final DoubleArray Y_, final DoubleArray X_, int dimension_) { final boolean commonX = (X_.numberOfDimensions() == 1 || (X_.numberOfDimensions() == 2 && ArrayUtils.contains(X_.size(), 1))); // Create an identically sizes array as this to hold the result final DoubleArray result = _arrayFactory.newArray(Y_.size()); Y_.across(new int[] { dimension_ }).reduce(new DoubleIndexedReduction() { @Override public double perform(DoubleSubspaceSplitIterator iterator_) throws RuntimeException { DoubleSubspaceSplitIterator subSpaceIterator_ = (DoubleSubspaceSplitIterator) iterator_; DoubleSettableIterator xIter; if (commonX) { xIter = (DoubleSettableIterator) X_.iterator(); } else { xIter = (DoubleSettableIterator) X_.iteratorAt(subSpaceIterator_.getFullIndex()); } if (!subSpaceIterator_.hasNext()) { return Double.NaN; } double y1 = subSpaceIterator_.nextDouble(); double x1 = xIter.nextDouble(); double priorXDiff = Double.NaN; int[] priorIndex = subSpaceIterator_.getFullIndex(); double priorGrad = Double.NaN; while (subSpaceIterator_.hasNext()) { double y2 = subSpaceIterator_.nextDouble(); double x2 = xIter.nextDouble(); double xDiff = x2 - x1; double currentGrad = (y2 - y1) / xDiff; // Use the weighted Harmonic mean a la Fritsch & Carlson, // except at the first point where it is just the current // grad double grad = Double.isNaN(priorGrad) ? currentGrad : // If the signs are different or one is 0, the outcome // should be 0 ((priorGrad * currentGrad <= 0) ? 0 : 3d * (xDiff + priorXDiff) / ((2d * xDiff + priorXDiff) / priorGrad + (xDiff + 2d * priorXDiff) / currentGrad)); result.set(grad, priorIndex); priorIndex = subSpaceIterator_.getFullIndex(); priorGrad = currentGrad; priorXDiff = xDiff; y1 = y2; x1 = x2; } // Need to set the final one too! - this only considers the // final // segment, so that extrapolation is linear result.set(priorGrad, priorIndex); return Double.NaN; } }); return result; } /** * Calculates the gradient of the grid along the given dimension, assuming * that the points on the grid are y-values and the points given in points_ * are x-values. *

* The grad is calculated as the weighted mean of the upper and lower * gradients except at the points on the boundary, where it is only the * upper or lower as appropriate. The weighting is linear by distance. The * gradient is the difference between two successive points divided by the * difference between the corresponding x values. * * @param Y_ * The function values * @param X_ * The x-values assumed at each grid point in the given * dimension. If X_ is one dimensional, they are constant across * other dimensions; if not, then X_ must be the same size as Y_ * and each point can have a value for X_. * @param dimension_ * The dimension along the specified dimension * * @return An array of the same dimension as this one with the gradient at * each point */ public static DoubleArray grad(final DoubleArray Y_, final DoubleArray X_, int dimension_) { // Create an identically sizes array as this to hold the result final DoubleArray result = _arrayFactory.newArray(Y_.size()); Y_.across(dimension_).reduce(new DoubleIndexedReduction() { @Override public double perform(DoubleSubspaceSplitIterator iterator_) throws RuntimeException { DoubleSettableIterator xIter = (DoubleSettableIterator) X_.iterator(); if (!iterator_.hasNext()) { return 0d; } double y1 = iterator_.nextDouble(); double x1 = xIter.nextDouble(); double priorXDiff = Double.NaN; int[] priorIndex = iterator_.getFullIndex(); double priorGrad = Double.NaN; while (iterator_.hasNext()) { double y2 = iterator_.nextDouble(); double x2 = xIter.nextDouble(); double xDiff = x2 - x1; double currentGrad = (y2 - y1) / xDiff; // The proportion to assign to the prior - note this is the // opposite of what it would // be if we were calculating the average gradient double priorProp = xDiff / (xDiff + priorXDiff); double grad = Double.isNaN(priorGrad) ? currentGrad : (priorGrad * priorProp + currentGrad * (1 - priorProp)); result.set(grad, priorIndex); priorIndex = iterator_.getFullIndex(); priorGrad = currentGrad; priorXDiff = xDiff; y1 = y2; x1 = x2; } // Need to set the final one too! result.set(priorGrad, priorIndex); return 0d; } }); return result; } public static DoubleArray interpolateFixedPoint(DoubleArray fn_, final DoubleArray levels_, int dimension_) { DoubleReduction fpOp = new DoubleReduction() { @Override public double perform(DoubleSettableIterator valuesIterator_) throws RuntimeException { DoubleSettableIndexedIterator levelIter; levelIter = levels_.iterator(); double lastX = Double.NEGATIVE_INFINITY; double lastY = Double.NEGATIVE_INFINITY; while (valuesIterator_.hasNext()) { double yVal = valuesIterator_.nextDouble(); double xVal = levelIter.nextDouble(); // Did we pass the fixed point? if (xVal - yVal > 1e-5) { // Linearly interpolate the fixed point return (lastX * (yVal - lastY) - lastY * (xVal - lastX)) / (yVal + lastX - lastY - xVal); } else { lastX = xVal; lastY = yVal; } } return Double.NaN; } }; return fn_.across(dimension_).reduce(fpOp); } /** * Finds the unique internal fixed point of the mapping from the regular * grid defined by the provided x values to the provided y values. *

* If there is no internal fixed point, or more than one, an * MultiDimensionalArrayException is thrown * * @param y_ * The y values of the mapping * @param xCoords_ * The x values along the dimensions of y that define the regular * grid on which the mapping is defined. The length of xCoords_ * must be equal to the number of dimensions of x_ minus 1, * unless y_ is one dimensional and xCoords_ has just one element * (since no additional dimension is required for the one- * dimensional mapping target) * * @return A one dimensional array of length ndim(y_) - 1 * * @throws com.meliorbis.numerics.generic.MultiDimensionalArrayException * If there are 0 or more than 1 internal fixed points, or * another error occurs */ public static DoubleArray interpolateFixedPoint(DoubleArray y_, DoubleArray[] xCoords_) throws NumericsException { DoubleArray yAdjusted = y_.copy(); for (int i = 0; i < xCoords_.length; i++) { // Subtract the appropriate inputs from each output to turn the // problem into a rootfinding one (y_.numberOfDimensions() == 1 ? yAdjusted : yAdjusted.lastDimSlice(i)).modifying().across(i).subtract(xCoords_[i]); } try { return findRoot(yAdjusted, xCoords_); } catch (NumericsException e_) { throw new NumericsException("No fixed point found", e_); } } /** * Finds the unique internal root of the mapping from the regular grid * defined by the provided x values to the provided y values. *

* If there is no root, or more than one, an NumericsException is thrown * * @param y_ * The y values of the mapping * @param xCoords_ * The x values along the dimensions of y that define the regular * grid on which the mapping is defined. The length of xCoords_ * must be equal to the number of dimensions of x_ minus 1, * unless y_ is one dimensional and xCoords_ has just one element * (since no additional dimension is required for the one- * dimensional mapping target) * * @return A one dimensional array of length ndim(y_) - 1 * * @throws NumericsException * If there are 0 or more than 1 internal roots, or another * error occurs */ public static DoubleArray findRoot(DoubleArray y_, DoubleArray[] xCoords_) throws NumericsException { final int nDims = xCoords_.length; // Short-cut - which also assumes monotonicity if (nDims == 1) { return Interpolation.interpDimension(xCoords_[0], y_, 0, 0); } double[] x_ = new double[nDims]; RealMatrix jacobean = MatrixUtils.createRealMatrix(nDims, nDims); double relError; int count = 0; if (nDims == 1) { // Start half way between points either side of 0 for (int i = 0; i < y_.numberOfElements() - 1; i++) { if (y_.get(i) * y_.get(i + 1) < 0) { x_[0] = (xCoords_[0].get(i) + xCoords_[0].get(i + 1)) / 2d; break; } } if (x_[0] == 0.0d) { throw new NumericsException("y values do not cross x axis"); } } else { for (int i = 0; i < nDims; i++) { // Subtract the appropriate inputs from each output to turn the // problem into a rootfinding one x_[i] = xCoords_[i].sum() / ((double) xCoords_[i].numberOfElements()) * 0.999; } } do { final Pair, DoubleArray> jacAndY = jacobeanAtPoint(y_, xCoords_, x_); final DoubleArray negError = jacAndY.getRight().multiply(-1d); // Set the jacobean rows for (int dim = 0; dim < nDims; dim++) { jacAndY.getLeft().at(dim, dim).modifying(); jacobean.setColumn(dim, jacAndY.getLeft().at(-1, dim).toArray()); } RealMatrix solved = new LUDecomposition(jacobean).getSolver().solve(MatrixUtils.createColumnRealMatrix(negError.toArray())); final double[] lastX = x_.clone(); relError = 0d; solved = solved.scalarMultiply(0.1); for (int dim = 0; dim < nDims; dim++) { final double adjustment = solved.getEntry(dim, 0); x_[dim] += adjustment; relError = Math.max(relError, Math.abs(lastX[dim] == 0d ? adjustment : adjustment / lastX[dim])); } } while (relError > 1e-10 && count++ < 10000); if (relError > 1e-10) { throw new NumericsException("No root found!"); } return _arrayFactory.newArray(x_); } /** * Calculates the jacobean of the mapping from the regular grid defined by * xCoords to y at the point x. The mapping is assumed to be piecewise * linear, and x must be internal. * * @param y_ * The y values of the mapping * @param xCoords_ * Coordinates along each dimension that define the regular grid * on which the mapping is specified * @param x_ * The point at which to get the jacobean. It must be internal to * the grid. * @return The jacobean at the specified point */ static Pair, DoubleArray> jacobeanAtPoint(DoubleArray y_, DoubleArray[] xCoords_, double[] x_) { /* * TODO: HIGHLY INEFFICIENT! FOR SPEED, CHANGE! */ int ndims = xCoords_.length; // Create interpolation specs for each dimension to the provided point // x_ List dimSpecs = new ArrayList(ndims); for (int dim = 0; dim < ndims; dim++) { dimSpecs.add(Interpolation.spec(dim, xCoords_[dim], x_[dim])); } final DoubleArray yAtX = Interpolation.interp(y_, dimSpecs.toArray(new Interpolation.Specification[ndims])); final DoubleArray result = _numerics.getArrayFactory().newArray(ndims, ndims); for (int dim = 0; dim < ndims; dim++) { // Don't interpolate along the dimension the grad is to be // calculated along final Interpolation.Specification removed = dimSpecs.remove(dim); final DoubleArray interpolated = Interpolation.interp(y_, dimSpecs.toArray(new Interpolation.Specification[ndims - 1])); final DoubleArray gradForDim = grad(interpolated, xCoords_[dim],/* * already * interpolated * to * here */0); final DoubleArray gradAtX = Interpolation.interpDimension(gradForDim, xCoords_[dim], x_[dim], 0); result.fillAt(gradAtX, -1, dim); // Put back the removed one for the nextDouble iteration dimSpecs.add(dim, removed); } return new Pair, DoubleArray>(result, yAtX); } /** * Interpolates a fixed point in a function (mapping) from one set of values * along a given dimension. *

* The method assumes that the input values are monotonic along the * dimension of interpolation * * @param x_ * The array of inputs, of arbitrary dimension * @param y_ * The array of function values, must be of the same dimension as * {@code x_} * @param dimension_ * The dimension along which to interpolate * * @return A value class containing three arrays: interpolated Y values, * indexes of the point below each target X, and the proportion to * assign to that lower point when distributing */ public static DoubleArray interpolateFixedPoints(DoubleArray x_, DoubleArray y_, int dimension_) { return interpolateFixedPoints(x_, y_, Double.NaN, dimension_); } /** * Interpolates a fixed point function (mapping) from one set of values * along a given dimension. *

* The method assumes that the input values are monotonic along the * dimension of interpolation * * @param x_ * The array of inputs, of arbitrary dimension * @param y_ * The array of function values, must be of the same dimension as * {@code x_} * * @param anchor_ * In the case that there are multiple fixed points, returns the * one closed to anchor * @param dimension_ * The dimension along which to interpolate * * @return A value class containing three arrays: interpolated Y values, * indexes of the point below each target X, and the proportion to * assign to that lower point when distributing */ public static DoubleArray interpolateFixedPoints(DoubleArray x_, DoubleArray y_, double anchor_, int dimension_) { // Get an iterator across the dimensions not being interpolated DoubleSubspaceSplitIterator nonInterpXIter = x_.iteratorAcross(new int[] { dimension_ }).getOrthogonalIterator(); DoubleSubspaceSplitIterator nonInterpYIter = y_.iteratorAcross(new int[] { dimension_ }).getOrthogonalIterator(); DoubleArray results = _arrayFactory.newArray(nonInterpXIter.getSizes()); DoubleSettableIndexedIterator resultIter = results.iterator(); while (nonInterpXIter.hasNext()) { nonInterpXIter.nextDouble(); nonInterpYIter.nextDouble(); resultIter.nextDouble(); // Get an iterator along the dimension we are to interpolate DoubleSubspaceSplitIterator interpDimXIter = nonInterpXIter.getOrthogonalIterator(); DoubleSubspaceSplitIterator interpDimYIter = nonInterpYIter.getOrthogonalIterator(); // Initial upper and lower X values; the upper X will definitely be // set at least once in the loop int[] currentIndex = interpDimXIter.getFullIndex(); currentIndex[dimension_]++; double priorX = Double.NaN; double priorY = Double.NaN; double priorDiff = Double.NaN; double minX = Double.NaN; double maxX = Double.NaN; List fixedPoints = new ArrayList(1); while (interpDimXIter.hasNext()) { double currentX = interpDimXIter.nextDouble(); double currentY = interpDimYIter.nextDouble(); double currentDiff = currentY - currentX; if (Double.isNaN(minX)) { minX = currentX; } // Are the signs of the two diffs the same? if (priorDiff * currentDiff < 0) { // The signs are different, i.e. one Y is lower than the X // and the other higher, hence they must cross-over // inbetween // Set the value and continue; fixedPoints.add((priorX * (currentY - priorY) - priorY * (currentX - priorX)) / (currentY - priorY - (currentX - priorX))); } // Move to the nextDouble point priorX = currentX; priorY = currentY; priorDiff = currentDiff; maxX = currentX; } if (fixedPoints.size() == 0) { resultIter.set(Double.NaN); } else if (fixedPoints.size() == 1) { resultIter.set(fixedPoints.get(0).doubleValue()); } else { double minDistanceFromEdge = Double.NEGATIVE_INFINITY; double currentMatch = Double.NaN; for (double fixedPoint : fixedPoints) { double distForPoint; if (Double.isNaN(anchor_)) { distForPoint = Math.min(fixedPoint - minX, maxX - fixedPoint); } else { distForPoint = -Math.abs(fixedPoint - anchor_); } if (distForPoint >= minDistanceFromEdge) { minDistanceFromEdge = distForPoint; currentMatch = fixedPoint; } } resultIter.set(currentMatch); } } return results; } public static DoubleUnaryOp cutToBounds(double min_, double max_) { return new CutToBounds(min_, max_); } /** * Returns the input if it is within the bounds, otherwise, the bound it is * beyond. * * @author toby */ public static final class CutToBounds implements DoubleUnaryOp { private final double _max; private final double _min; private CutToBounds(double min_, double max_) { _max = max_; _min = min_; } @Override public double perform(double input_) { return (input_ < _min) ? _min : (input_ > _max) ? _max : input_; } } public static double maximumRelativeDifferenceSpecial(DoubleArray a_, DoubleArray b_) { double criterion; DoubleSettableIndexedIterator bIter = b_.iterator(); DoubleSettableIndexedIterator aIter = a_.iterator(); criterion = 0d; while (bIter.hasNext()) { double newVal = aIter.nextDouble(); double oldVal = bIter.nextDouble(); if (Math.abs(oldVal) > 1e-5 && Math.abs(newVal) > 1e-5) { criterion = Math.max(criterion, Math.abs((newVal - oldVal) / (1 + Math.abs(oldVal)))); } else { criterion = Math.max(criterion, Math.abs(newVal - oldVal)); } } return criterion; } public static double maximumRelativeDifference(DoubleArray aggKapitalTransitionRule, DoubleArray capitalTransition) { double criterion; DoubleSettableIndexedIterator newTransIter = capitalTransition.iterator(); DoubleSettableIndexedIterator oldTransIter = aggKapitalTransitionRule.iterator(); criterion = 0d; while (newTransIter.hasNext()) { double oldVal = oldTransIter.nextDouble(); double newVal = newTransIter.nextDouble(); if (Math.abs(oldVal) > 1e-5 && Math.abs(newVal) > 1e-5) { criterion = Math.max(criterion, Math.abs(newVal / oldVal - 1)); } else { criterion = Math.max(criterion, Math.abs(newVal - oldVal)); } } return criterion; } /** * Given two multi-dimensional transition matrices, combines them as though * they were independent. * * @param firstTrans_ * The first transition matrix. Must have as many source as * target states. * @param secondTrans_ * The second transition matrix. Must have as many source as * target states. * * @return A transition matrix between the combined sets of states */ public static DoubleArray combineTransitions(DoubleArray firstTrans_, DoubleArray secondTrans_) { int[] firstSize = firstTrans_.size(); int[] secondSize = secondTrans_.size(); int[] resultSize = new int[firstSize.length + secondSize.length]; int firstSizeElements = firstSize.length / 2; System.arraycopy(firstSize, 0, resultSize, 0, firstSizeElements); int secondSizeElements = secondSize.length / 2; System.arraycopy(secondSize, 0, resultSize, firstSizeElements, secondSizeElements); System.arraycopy(firstSize, firstSizeElements, resultSize, firstSizeElements + secondSizeElements, firstSizeElements); System.arraycopy(secondSize, secondSizeElements, resultSize, firstSize.length + secondSizeElements, secondSizeElements); DoubleArray result = _arrayFactory.newArray(resultSize); result.fillDimensions( firstTrans_, ArrayUtils.addAll(Utils.sequence(0, firstSizeElements), Utils.sequence(firstSizeElements + secondSizeElements, 2 * firstSizeElements + secondSizeElements))); return result.across( ArrayUtils.addAll(Utils.sequence(firstSizeElements, firstSizeElements + secondSizeElements), Utils.sequence(2 * firstSizeElements + secondSizeElements, 2 * firstSizeElements + 2 * secondSizeElements))).multiply( secondTrans_); } public static final DoubleUnaryOp pow(final double power_) { return x -> Math.pow(x, power_); } public static final DoubleUnaryOp abs = x -> Math.abs(x); public static final DoubleUnaryOp exp = x -> Math.exp(x); public static final DoubleUnaryOp log = x -> Math.log(x); public static final DoubleNaryOp weightedMean(final double... weights_) { double finalWeightCalc = 1d; for (int i = 0; i < weights_.length; i++) { finalWeightCalc -= weights_[i]; } final double finalWeight = finalWeightCalc; return inputs_ -> { double sum = 0d; for (int i = 0; i < weights_.length; i++) { sum += inputs_[i] * weights_[i]; } sum += finalWeight * inputs_[weights_.length]; return sum; }; } /** * Gets a sequence of values that are numLogs-log distributed, i.e. * e^numLogs times of the values will be evenly distributed * * @param maxValue_ * The maximum value * @param numValues_ * The number of values * @param numLogs_ * How many times to apply log * * @return A one dimensional array of values as described */ static public DoubleArray nLogSequence(double maxValue_, int numValues_, int numLogs_) { int logCount = 0; // Take the log a number of times specific, adding 1 at each juncture // (because log 1 = 0, but we actually want the base sequence to have a // 0) while (logCount++ < numLogs_) { maxValue_ = Math.log(maxValue_ + 1); } // Create an equally spaced sequence between 0 and the modified maxValue double[] capLevels = Utils.sequence(0d, maxValue_, numValues_); // Create an array with the values DoubleArray values = _numerics.getArrayFactory().newArray(capLevels); // Now reverse the previous step on each value while (--logCount > 0) { values.modifying().map((DoubleUnaryOp) input_ -> Math.exp(input_) - 1); } return values; } /** * Creates an array where values are more tightly clustered around the mean * * @param values_ * The array to pull * @param maxMeanWeight_ * The maximum proportional weight to give to the mean when * calculating the new value at each point. Points furthest from * the mean are not pulled in * @return The pulled array */ static public DoubleArray pullToMean(final DoubleArray values_, final double maxMeanWeight_) { final double meanValue = values_.sum() / ((double) values_.numberOfElements()); final double distToMax = values_.max() - meanValue; final double distToMin = meanValue - values_.min(); final double highestDist = Math.max(distToMax, distToMin); DoubleArray individualTransition = values_.map((DoubleUnaryOp) input_ -> { double meanWeight = (1 - Math.abs(meanValue - input_) / highestDist) * maxMeanWeight_; return input_ * (1d - meanWeight) + meanValue * meanWeight; }); return individualTransition; } /** * Creates an array where each value is pulled closer to the mean by the * same proportion * * @param values_ * The array to squeeze * * @param meanWeight_ * The proportional weight to give to the mean when calculating * the new value at each point * * @return The squeezed array */ static public DoubleArray squeezeToMean(final DoubleArray values_, final double meanWeight_) { return squeezeTo(values_, values_.sum() / ((double) values_.numberOfElements()), meanWeight_); } /** * Creates an array where each value is pulled closer to the target by the * same proportion * * @param values_ * The array to squeeze * * @param target_ * The target to squeeze toward * * @param targetWeight_ * The proportional weight to give to the mean when calculating * the new value at each point * * @return The squeezed array */ static public DoubleArray squeezeTo(final DoubleArray values_, final double target_, final double targetWeight_) { final double weightedTarget = targetWeight_ * target_; return values_.map((DoubleUnaryOp) input_ -> input_ * (1d - targetWeight_) + weightedTarget); } public static void ensureAllEqualSize(MultiDimensionalArray[] arrays_) { int[] size = arrays_[0].size(); ensureAllOfSize(size, arrays_, 1); } public static void ensureAllEqualSize(MultiDimensionalArray reference_, MultiDimensionalArray[] arrays_) { int[] size = reference_.size(); ensureAllOfSize(size, arrays_, 0); } private static void ensureAllOfSize(int[] size_, MultiDimensionalArray[] arrays_, int startAt_) { for (int i = startAt_; i < arrays_.length; i++) { if (!equalSize(size_, arrays_[i].size())) { throw new MultiDimensionalArrayException("Arrays must be of same size"); } } } static private boolean equalSize(int[] a_, int[] b_) { int aIndex = 0; int bIndex = 0; // Iterate over the first size array while (aIndex < a_.length) { // If we are done with the second one already, the first one must // only have 1s if (bIndex == b_.length) { if (a_[aIndex] == 1) { aIndex++; continue; } return false; } // Standard case: they are the same size if (a_[aIndex] == b_[bIndex]) { aIndex++; bIndex++; continue; } // Otherwise, one of them may be of size 1, so a 'skipping' // dimension if (a_[aIndex] == 1) { aIndex++; continue; } if (b_[bIndex] == 1) { bIndex++; continue; } // Oops, not the same return false; } // If there are still elements of b left they must be ones while (bIndex < b_.length) { if (b_[bIndex++] != 1) { return false; } } return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy