use of gdsc.smlm.results.filter.FilterScore in project GDSC-SMLM by aherbert.
the class BenchmarkFilterAnalysis method parameterAnalysis.
/**
* Run the optimum filter on a set of labelled peak results using various parameter settings outputting performance
* statistics on the success of the filter to an ImageJ table.
* <p>
* If a new optimum is found the class level static parameters are updated.
*
* @param nonInteractive
* True if non interactive
* @param currentOptimum
* the optimum
* @param rangeReduction
* the range reduction
* @return the best filter
*/
private ComplexFilterScore parameterAnalysis(boolean nonInteractive, ComplexFilterScore currentOptimum, double rangeReduction) {
this.ga_resultsList = resultsList;
String algorithm = "";
// All the search algorithms use search dimensions.
ss_filter = currentOptimum.r.filter;
FixedDimension[] originalDimensions = new FixedDimension[3];
double[] point = createParameters();
String[] names = { "Fail count", "Residuals threshold", "Duplicate distance" };
{
// Local scope for i
int i = 0;
try {
originalDimensions[i++] = new FixedDimension(minFailCount, maxFailCount, 1);
// TODO - let the min intervals be configured, maybe via extra options
if (BenchmarkSpotFit.computeDoublets)
originalDimensions[i++] = new FixedDimension(minResidualsThreshold, maxResidualsThreshold, 0.05);
else
originalDimensions[i++] = new FixedDimension(1, 1, 0.05);
originalDimensions[i++] = new FixedDimension(minDuplicateDistance, maxDuplicateDistance, 0.5);
} catch (IllegalArgumentException e) {
Utils.log(TITLE + " : Unable to configure dimension [%d] %s: " + e.getMessage(), i, names[i]);
return null;
}
}
// Check for a search
boolean active = false;
for (int i = 0; i < originalDimensions.length; i++) {
if (originalDimensions[i].isActive()) {
active = true;
break;
}
}
if (!active) {
Utils.log(TITLE + " : No search range");
return currentOptimum;
}
// Optionally use a reduced range (this is used for iteration)
if (rangeReduction > 0 && rangeReduction < 1) {
// Suppress dialogs and use the current settings
nonInteractive = true;
for (int i = 0; i < originalDimensions.length; i++) {
double centre = point[i];
double r = 0;
if (originalDimensions[i].isActive()) {
r = (originalDimensions[i].max - originalDimensions[i].min) * rangeReduction;
}
double lower = centre - r * 0.5;
double upper = centre + r * 0.5;
originalDimensions[i] = originalDimensions[i].create(lower, upper);
}
}
analysisStopWatch = StopWatch.createStarted();
// Store this for later debugging
SearchResult<FilterScore> optimum = null;
if (searchParam == 0 || searchParam == 2) {
// Collect parameters for the range search algorithm
pauseParameterTimer();
boolean isStepSearch = searchParam == 2;
// The step search should use a multi-dimension refinement and no range reduction
SearchSpace.RefinementMode myRefinementMode = SearchSpace.RefinementMode.MULTI_DIMENSION;
GenericDialog gd = null;
boolean runAlgorithm = nonInteractive;
if (!nonInteractive) {
// Ask the user for the search parameters.
gd = new GenericDialog(TITLE);
gd.addMessage("Configure the " + SEARCH[searchParam] + " algorithm for " + ss_filter.getType());
gd.addSlider("Width", 1, 5, pRangeSearchWidth);
if (!isStepSearch) {
gd.addNumericField("Max_iterations", pMaxIterations, 0);
String[] modes = SettingsManager.getNames((Object[]) SearchSpace.RefinementMode.values());
gd.addSlider("Reduce", 0.01, 0.99, pRangeSearchReduce);
gd.addChoice("Refinement", modes, modes[pRefinementMode]);
}
gd.addNumericField("Seed_size", pSeedSize, 0);
gd.showDialog();
runAlgorithm = !gd.wasCanceled();
}
if (runAlgorithm) {
SearchDimension[] dimensions = new SearchDimension[originalDimensions.length];
if (!nonInteractive) {
pRangeSearchWidth = (int) gd.getNextNumber();
if (!isStepSearch) {
pMaxIterations = (int) gd.getNextNumber();
pRangeSearchReduce = gd.getNextNumber();
pRefinementMode = gd.getNextChoiceIndex();
}
pSeedSize = (int) gd.getNextNumber();
}
if (!isStepSearch)
myRefinementMode = SearchSpace.RefinementMode.values()[pRefinementMode];
for (int i = 0; i < dimensions.length; i++) {
if (originalDimensions[i].isActive()) {
try {
dimensions[i] = originalDimensions[i].create(pRangeSearchWidth);
dimensions[i].setPad(true);
// Prevent range reduction so that the step search just does a single refinement step
dimensions[i].setReduceFactor((isStepSearch) ? 1 : pRangeSearchReduce);
// Centre on current optimum
dimensions[i].setCentre(point[i]);
} catch (IllegalArgumentException e) {
IJ.error(TITLE, String.format("Unable to configure dimension [%d] %s: " + e.getMessage(), i, names[i]));
return null;
}
} else {
dimensions[i] = new SearchDimension(point[i]);
}
}
// Check the number of combinations is OK
long combinations = SearchSpace.countCombinations(dimensions);
if (!nonInteractive && combinations > 10000) {
gd = new GenericDialog(TITLE);
gd.addMessage(String.format("%d combinations for the configured dimensions.\n \nClick 'Yes' to optimise.", combinations));
gd.enableYesNoCancel();
gd.hideCancelButton();
gd.showDialog();
if (!gd.wasOKed()) {
combinations = 0;
}
}
if (combinations == 0) {
resumeParameterTimer();
} else {
algorithm = SEARCH[searchParam] + " " + pRangeSearchWidth;
ga_statusPrefix = algorithm + " " + ss_filter.getName() + " ... ";
ga_iteration = 0;
p_optimum = null;
SearchSpace ss = new SearchSpace();
ss.setTracker(this);
if (pSeedSize > 0) {
// Add current optimum to seed
// Note: If we have an optimum and we are not seeding this should not matter as the dimensions
// have been centred on the current optimum
double[][] seed = new double[1][];
seed[0] = point;
// Sample without rounding as the seed will be rounded
double[][] sample = SearchSpace.sampleWithoutRounding(dimensions, pSeedSize - 1, null);
ss.seed(merge(sample, seed));
}
ConvergenceChecker<FilterScore> checker = new InterruptConvergenceChecker(0, 0, pMaxIterations);
createGAWindow();
resumeParameterTimer();
optimum = ss.search(dimensions, new ParameterScoreFunction(), checker, myRefinementMode);
if (optimum != null) {
// In case optimisation was stopped
IJ.resetEscape();
// Now update the parameters for final assessment
point = optimum.point;
// Not required as the seed in now rounded
//if (pSeedSize > 0)
//{
// // The optimum may be off grid if it was from the seed
// point = enumerateMinInterval(point, names, originalDimensions);
//}
}
}
} else
resumeParameterTimer();
}
if (searchParam == 1) {
// Collect parameters for the enrichment search algorithm
pauseParameterTimer();
GenericDialog gd = null;
boolean runAlgorithm = nonInteractive;
if (!nonInteractive) {
// Ask the user for the search parameters.
gd = new GenericDialog(TITLE);
gd.addMessage("Configure the " + SEARCH[searchParam] + " algorithm for " + ss_filter.getType());
gd.addNumericField("Max_iterations", pMaxIterations, 0);
gd.addNumericField("Converged_count", pConvergedCount, 0);
gd.addNumericField("Samples", pEnrichmentSamples, 0);
gd.addSlider("Fraction", 0.01, 0.99, pEnrichmentFraction);
gd.addSlider("Padding", 0, 0.99, pEnrichmentPadding);
gd.showDialog();
runAlgorithm = !gd.wasCanceled();
}
if (runAlgorithm) {
FixedDimension[] dimensions = Arrays.copyOf(originalDimensions, originalDimensions.length);
if (!nonInteractive) {
pMaxIterations = (int) gd.getNextNumber();
pConvergedCount = (int) gd.getNextNumber();
pEnrichmentSamples = (int) gd.getNextNumber();
pEnrichmentFraction = gd.getNextNumber();
pEnrichmentPadding = gd.getNextNumber();
}
algorithm = SEARCH[searchParam];
ga_statusPrefix = algorithm + " " + ss_filter.getName() + " ... ";
ga_iteration = 0;
p_optimum = null;
SearchSpace ss = new SearchSpace();
ss.setTracker(this);
// Add current optimum to seed
double[][] seed = new double[1][];
seed[0] = point;
ss.seed(seed);
ConvergenceChecker<FilterScore> checker = new InterruptConvergenceChecker(0, 0, pMaxIterations, pConvergedCount);
createGAWindow();
resumeParameterTimer();
optimum = ss.enrichmentSearch(dimensions, new ParameterScoreFunction(), checker, pEnrichmentSamples, pEnrichmentFraction, pEnrichmentPadding);
if (optimum != null) {
// In case optimisation was stopped
IJ.resetEscape();
point = optimum.point;
// Not required as the search now respects the min interval
// Enumerate on the min interval to produce the final filter
//point = enumerateMinInterval(point, names, originalDimensions);
}
} else
resumeParameterTimer();
}
if (searchParam == 3) {
// Collect parameters for the enumeration search algorithm
pauseParameterTimer();
SearchDimension[] dimensions = new SearchDimension[originalDimensions.length];
for (int i = 0; i < dimensions.length; i++) {
if (originalDimensions[i].isActive()) {
try {
dimensions[i] = originalDimensions[i].create(0);
} catch (IllegalArgumentException e) {
IJ.error(TITLE, String.format("Unable to configure dimension [%d] %s: " + e.getMessage(), i, names[i]));
return null;
}
} else {
dimensions[i] = new SearchDimension(point[i]);
}
}
GenericDialog gd = null;
long combinations = SearchSpace.countCombinations(dimensions);
if (!nonInteractive && combinations > 2000) {
gd = new GenericDialog(TITLE);
gd.addMessage(String.format("%d combinations for the configured dimensions.\n \nClick 'Yes' to optimise.", combinations));
gd.enableYesNoCancel();
gd.hideCancelButton();
gd.showDialog();
if (!gd.wasOKed()) {
combinations = 0;
}
}
if (combinations == 0) {
resumeParameterTimer();
} else {
algorithm = SEARCH[searchParam];
ga_statusPrefix = algorithm + " " + ss_filter.getName() + " ... ";
ga_iteration = 0;
p_optimum = null;
SearchSpace ss = new SearchSpace();
ss.setTracker(this);
createGAWindow();
resumeParameterTimer();
optimum = ss.findOptimum(dimensions, new ParameterScoreFunction());
if (optimum != null) {
// In case optimisation was stopped
IJ.resetEscape();
// Now update the parameters for final assessment
point = optimum.point;
}
}
}
IJ.showStatus("Analysing " + ss_filter.getName() + " ...");
// Update the parameters using the optimum
failCount = (int) Math.round(point[0]);
residualsThreshold = sResidualsThreshold = point[1];
duplicateDistance = point[2];
// Refresh the coordinate store
if (coordinateStore == null || duplicateDistance != coordinateStore.getResolution()) {
coordinateStore = createCoordinateStore();
}
createResultsPrefix2();
// (Re) Score the filter.
// TODO - check this is now OK. Maybe remove the enumeration on the min interval grid
// If scoring of filter here is different to scoring in the optimisation routine it is probably an ss_filter.clone() issue,
// i.e. multi-threading use of the filter clone is not working.
// Or it could be that the optimisation produced params off the min-interval grid
FilterScoreResult scoreResult = scoreFilter(ss_filter);
if (optimum != null) {
if (scoreResult.score != optimum.score.score && scoreResult.criteria != optimum.score.criteria) {
ParameterScoreResult r = scoreFilter((DirectFilter) ss_filter.clone(), minimalFilter, failCount, residualsThreshold, duplicateDistance, createCoordinateStore(duplicateDistance), false);
System.out.printf("Weird re- score of the filter: %f!=%f or %f!=%f (%f:%f)\n", scoreResult.score, optimum.score.score, scoreResult.criteria, optimum.score.criteria, r.score, r.criteria);
}
}
SimpleFilterScore max = new SimpleFilterScore(scoreResult, true, scoreResult.criteria >= minCriteria);
analysisStopWatch.stop();
if (showResultsTable) {
BufferedTextWindow tw = null;
if (resultsWindow != null)
tw = new BufferedTextWindow(resultsWindow);
addToResultsWindow(tw, scoreResult.text);
if (resultsWindow != null)
resultsWindow.getTextPanel().updateDisplay();
}
// Check the top result against the limits of the original dimensions
StringBuilder sb = new StringBuilder(200);
for (int j = 0; j < originalDimensions.length; j++) {
if (!originalDimensions[j].isActive())
continue;
final double value = point[j];
double lowerLimit = originalDimensions[j].getLower();
double upperLimit = originalDimensions[j].getUpper();
int c1 = Double.compare(value, lowerLimit);
if (c1 <= 0) {
sb.append(" : ").append(names[j]).append(' ').append(ComplexFilterScore.FLOOR).append('[').append(Utils.rounded(value));
if (c1 == -1) {
sb.append("<").append(Utils.rounded(lowerLimit));
}
sb.append("]");
} else {
int c2 = Double.compare(value, upperLimit);
if (c2 >= 0) {
sb.append(" : ").append(names[j]).append(' ').append(ComplexFilterScore.CEIL).append('[').append(Utils.rounded(value));
if (c2 == 1) {
sb.append(">").append(Utils.rounded(upperLimit));
}
sb.append("]");
}
}
}
if (sb.length() > 0) {
if (max.criteriaPassed) {
Utils.log("Warning: Top filter (%s @ %s|%s) [%s] at the limit of the expanded range%s", ss_filter.getName(), Utils.rounded((invertScore) ? -max.score : max.score), Utils.rounded((invertCriteria) ? -minCriteria : minCriteria), limitFailCount + limitRange, sb.toString());
} else {
Utils.log("Warning: Top filter (%s @ -|%s) [%s] at the limit of the expanded range%s", ss_filter.getName(), Utils.rounded((invertCriteria) ? -max.criteria : max.criteria), limitFailCount + limitRange, sb.toString());
}
}
// We may have no filters that pass the criteria
String type = max.r.filter.getType();
if (!max.criteriaPassed) {
Utils.log("Warning: Filter does not pass the criteria: %s : Best = %s using %s", type, Utils.rounded((invertCriteria) ? -max.criteria : max.criteria), max.r.filter.getName());
return null;
}
// Update without duplicates
boolean allowDuplicates = false;
// Re-use the atLimit and algorithm for the input optimum
ComplexFilterScore newFilterScore = new ComplexFilterScore(max.r, currentOptimum.atLimit, currentOptimum.algorithm, currentOptimum.time, algorithm, analysisStopWatch.getTime());
addBestFilter(type, allowDuplicates, newFilterScore);
// Add spacer at end of each result set
if (isHeadless) {
if (showResultsTable)
IJ.log("");
} else {
if (showResultsTable)
resultsWindow.append("");
}
if (newFilterScore.compareTo(currentOptimum) <= 0)
return newFilterScore;
else {
// Update the algorithm and time
currentOptimum.paramAlgorithm = algorithm;
currentOptimum.paramTime = analysisStopWatch.getTime();
}
return currentOptimum;
}
Aggregations