Patching bug in digamma function which is susceptible to race condition in multi-threaded applications. Also explicitly setting return array values in Kraskov calculators.

This commit is contained in:
joseph.lizier 2014-11-14 10:24:53 +00:00
parent 2a36ac0387
commit 24a0566fd4
3 changed files with 49 additions and 18 deletions

View File

@ -513,7 +513,9 @@ public abstract class ConditionalMutualInfoCalculatorMultiVariateKraskov
System.out.printf("Av = digamma(k)=%.3f + <digammas>=%.3f = %.3f \n",
MathsUtils.digamma(k), averageDiGammas, MathsUtils.digamma(k) + averageDiGammas);
}
return new double[] { MathsUtils.digamma(k) + averageDiGammas };
double[] result = new double[1];
result[0] = MathsUtils.digamma(k) + averageDiGammas;
return result;
} else {
// Algorithm 2:
// We also retrieve the sums of inverses for debugging purposes:
@ -531,7 +533,9 @@ public abstract class ConditionalMutualInfoCalculatorMultiVariateKraskov
(2.0 / (double)k), averageMeasure,
averageInverseCountInJointYZ, averageInverseCountInJointXZ);
}
return new double[] { averageMeasure };
double[] result = new double[1];
result[0] = averageMeasure;
return result;
}
}
}

View File

@ -204,8 +204,10 @@ public class ConditionalMutualInfoCalculatorMultiVariateKraskov1
if (returnLocals) {
localCondMi[t-startTimePoint] = digammaK - digammaNxzPlusOne - digammaNyzPlusOne + digammaNzPlusOne;
if (debug) {
System.out.printf("t=%d, n_xz=%d, n_yz=%d, n_z=%d, local=%.4f\n",
t, n_xz, n_yz, n_z, localCondMi[t-startTimePoint]);
System.out.printf("t=%d, n_xz=%d, n_yz=%d, n_z=%d, local=%.4f," +
" digamma(n_xz+1)=%.5f, digamma(n_yz+1)=%.5f, digamma(n_z+1)=%.5f, \n",
t, n_xz, n_yz, n_z, localCondMi[t-startTimePoint],
digammaNxzPlusOne, digammaNyzPlusOne, digammaNzPlusOne);
}
}
}
@ -221,6 +223,9 @@ public class ConditionalMutualInfoCalculatorMultiVariateKraskov1
System.out.println("\tz search: " + (conditionalTime/1000.0));
System.out.println("\tzx search: " + (conditionalXTime/1000.0));
System.out.println("\tzy search: " + (conditionalYTime/1000.0));
System.out.printf("%d:%d -- Returning: %.4f, %.4f, %.4f, %.4f\n",
startTimePoint, (startTimePoint + numTimePoints),
sumDiGammas, sumNxz, sumNyz, sumNz);
}
// Select what to return:
@ -229,7 +234,12 @@ public class ConditionalMutualInfoCalculatorMultiVariateKraskov1
} else {
// Pad return array with two values, to allow compatibility in
// return length with algorithm 2
return new double[] {sumDiGammas, sumNxz, sumNyz, sumNz, 0, 0};
double[] results = new double[6];
results[0] = sumDiGammas;
results[1] = sumNxz;
results[2] = sumNyz;
results[3] = sumNz;
return results;
}
}
}

View File

@ -259,10 +259,10 @@ public class MathsUtils {
}
/**
* Compute digamma(d).
* <p>Compute digamma(d).</p>
*
* Stores previous calculations to speed up computation here, though some precision may
* be lost because we're adding in larger numbers first.
* <p>Stores previous calculations to speed up computation here, though some precision may
* be lost because we're adding in larger numbers first.</p>
*
* @param d
* @return
@ -272,13 +272,15 @@ public class MathsUtils {
if (d < 1) {
return Double.NaN;
}
if (highestDigammaArgCalced == 0) {
// We're not initialised yet:
storedDigammas[0] = Double.NaN;
storedDigammas[1] = -EULER_MASCHERONI_CONSTANT;
highestDigammaArgCalced = 1;
}
if (d <= highestDigammaArgCalced) {
// Need to take care with multi-threaded applications
// here -- another thread could be attempting to update
// highestDigammaArgCalced at the same time.
// Sample the value of highestDigammaArgCalced and
// run with it through the rest of the method, to avoid
// any race conditions if it is concurrently updated:
int highestDigammaArgCalcedAtStartForThisThread =
highestDigammaArgCalced;
if (d <= highestDigammaArgCalcedAtStartForThisThread) {
// We've already calculated this one
return storedDigammas[d];
}
@ -287,14 +289,29 @@ public class MathsUtils {
// directly use commons.math:
return Gamma.digamma(d);
}
if (highestDigammaArgCalcedAtStartForThisThread == 0) {
// We're not initialised yet:
storedDigammas[0] = Double.NaN;
storedDigammas[1] = -EULER_MASCHERONI_CONSTANT;
highestDigammaArgCalcedAtStartForThisThread = 1;
}
// Else we'll calculate it and update the storage:
double result = storedDigammas[highestDigammaArgCalced];
for (int n = highestDigammaArgCalced + 1; n <= d; n++) {
double result = storedDigammas[highestDigammaArgCalcedAtStartForThisThread];
for (int n = highestDigammaArgCalcedAtStartForThisThread + 1; n <= d; n++) {
result += 1.0 / (double) (n-1);
// n must be < NUM_STORED_DIGAMMAS by earlier if statement on d
storedDigammas[n] = result;
}
highestDigammaArgCalced = d;
// Finally, update highestDigammaArgCalced:
// We could synchronize on highestDigammaArgCalced, however
// this is costly; the following check may be compromised under
// a race condition, but the worst outcome is that we have to
// recalculate a few values -- this is probably faster than
// enforcing synchronisation
if (d > highestDigammaArgCalced) {
// We make the check in case another thread has already set this higher
highestDigammaArgCalced = d;
}
return result;
}