/******************************************************************************* * Copyright 2012 Analog Devices, Inc. * * 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 com.analog.lyric.math; import java.util.Random; public class Utilities { /* * This method expects a PMF and a random number generator and returns a sample from that * distribution. The PMF does not actually have to be normalized for this to work. */ public static final int sampleFromMultinomial(double[] unnormalizedPMF, Random rand) { int M = unnormalizedPMF.length; // Calculate cumulative conditional probability (unnormalized) int M2 = nextPow2(M); // Round up array size to next power of two for subsequent binary search double[] cumulative = new double[M2]; double sum = 0; cumulative[0] = 0; for (int m = 1; m < M; m++) { sum += unnormalizedPMF[m-1]; cumulative[m] = sum; } sum += unnormalizedPMF[M-1]; for (int m = M; m < M2; m++) cumulative[m] = Double.POSITIVE_INFINITY; // Sample from the distribution using a binary search. double randomValue = sum * rand.nextDouble(); int sampleIndex = 0; int half = M2 >> 1; for (int bitValue = half; bitValue > 0; bitValue >>= 1) { int testIndex = sampleIndex | bitValue; if (randomValue > cumulative[testIndex]) sampleIndex = testIndex; } return sampleIndex; } /** * Round up to the next power of 2 * @param x is a non-negative value */ public static final int nextPow2(int x) { --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return ++x; } /** * Find the position of the most significant bit. */ public static final int findMSB(int value) { // This might be faster depending on the JVM: // return 32 - Integer.numberOfLeadingZeros(x); // Also, this currently returns 1-based index. Perhaps 0-based would be better w/ -1 for zero case. int count = 0; while (value != 0) { value >>= 1; count++; } return count; } // Log base 2 private static final double LOG2 = Math.log(2); /** * Computes log base 2 of input. */ public static final double log2(double x) { return Math.log(x)/LOG2; } /** * Normalizes weights to sum to one. * <p> * Modifies the weights in the array by dividing by their sum. * Does nothing if the sum of the weights is zero. * <p> * @param weights a non-empty array of positive weights. * @return {@code weights} array that was passed in * @since 0.08 */ public static double[] normalize(double[] weights) { final int n = weights.length; double sum = 0.0; for (int i = n; --i>=0;) sum += weights[i]; if (sum != 0.0) { for (int i = n; --i>=0;) weights[i] /= sum; } return weights; } /** * Convert value from energy domain to weight/probability domain. * <p> * This simply returns: * <blockquote> * <big> * e<sup>-energy</sup> * </big> * </blockquote> * <p> * @param energy is any value. Positive infinity corresponds to a zero weight, * and zero corresponds to a weight of one. * @see #weightToEnergy(double) */ public static double energyToWeight(double energy) { return Math.exp(-energy); } /** * Convert value from weight/probability domain to energy domain. * <p> * This is simply the negative natural log of the weight: * <blockquote> * -ln(weight) * </blockquote> * <p> * @param weight is a non-negative value. * @since 0.08 */ public static double weightToEnergy(double weight) { return -Math.log(weight); } }