Search in sources :

Example 6 with FilterScore

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;
}
Also used : SearchSpace(gdsc.smlm.search.SearchSpace) GenericDialog(ij.gui.GenericDialog) NonBlockingGenericDialog(ij.gui.NonBlockingGenericDialog) BufferedTextWindow(gdsc.core.ij.BufferedTextWindow) SearchDimension(gdsc.smlm.search.SearchDimension) FixedDimension(gdsc.smlm.search.FixedDimension) FilterScore(gdsc.smlm.results.filter.FilterScore)

Aggregations

FilterScore (gdsc.smlm.results.filter.FilterScore)6 SearchDimension (gdsc.smlm.search.SearchDimension)4 SearchSpace (gdsc.smlm.search.SearchSpace)4 DirectFilter (gdsc.smlm.results.filter.DirectFilter)3 IDirectFilter (gdsc.smlm.results.filter.IDirectFilter)3 BufferedTextWindow (gdsc.core.ij.BufferedTextWindow)2 FractionClassificationResult (gdsc.core.match.FractionClassificationResult)2 FilterSet (gdsc.smlm.results.filter.FilterSet)2 FixedDimension (gdsc.smlm.search.FixedDimension)2 GenericDialog (ij.gui.GenericDialog)2 NonBlockingGenericDialog (ij.gui.NonBlockingGenericDialog)2 MaximaSpotFilter (gdsc.smlm.filters.MaximaSpotFilter)1 RampedSelectionStrategy (gdsc.smlm.ga.RampedSelectionStrategy)1 SimpleMutator (gdsc.smlm.ga.SimpleMutator)1 SimpleRecombiner (gdsc.smlm.ga.SimpleRecombiner)1 SimpleSelectionStrategy (gdsc.smlm.ga.SimpleSelectionStrategy)1 Filter (gdsc.smlm.results.filter.Filter)1 MultiPathFilter (gdsc.smlm.results.filter.MultiPathFilter)1 ParameterType (gdsc.smlm.results.filter.ParameterType)1 SearchResult (gdsc.smlm.search.SearchResult)1