package gdsc.smlm.results.filter; /*----------------------------------------------------------------------------- * GDSC SMLM Software * * Copyright (C) 2013 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. *---------------------------------------------------------------------------*/ import java.util.Arrays; import org.apache.commons.lang3.NotImplementedException; import com.thoughtworks.xstream.annotations.XStreamOmitField; import gdsc.smlm.results.MemoryPeakResults; import gdsc.smlm.results.PeakResult; /** * Filter results using the combination of two filters. * <p> * Note that is the filter is not a DirectFilter then the result of filtering a PreprocessedPeakResult is always true * using that filter. */ public abstract class CombinedFilter extends DirectFilter { protected Filter filter1, filter2; @XStreamOmitField protected DirectFilter dfilter1; @XStreamOmitField protected DirectFilter dfilter2; @XStreamOmitField protected int result1; @XStreamOmitField protected int result2; public CombinedFilter(Filter filter1, Filter filter2) { this.filter1 = filter1; this.filter2 = filter2; initialiseState(); } @Override protected void initialiseState() { if (filter1 instanceof DirectFilter) dfilter1 = (DirectFilter) filter1; if (filter2 instanceof DirectFilter) dfilter2 = (DirectFilter) filter2; } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#clone() */ @Override public Filter clone() { // Add a reminder to implement clone throw new NotImplementedException("Derived classes must clone filter1 and filter2"); } @Override protected String generateName() { StringBuilder sb = new StringBuilder(); addText(sb, filter1, filter1.getName()); sb.append(" ").append(getOperator()).append(" "); addText(sb, filter2, filter2.getName()); return sb.toString(); } private void addText(StringBuilder sb, Filter f, String text) { if (f instanceof CombinedFilter) sb.append("("); sb.append(text); if (f instanceof CombinedFilter) sb.append(")"); } @Override protected String generateType() { StringBuilder sb = new StringBuilder(); addText(sb, filter1, filter1.getType()); sb.append(" ").append(getOperator()).append(" "); addText(sb, filter2, filter2.getType()); return sb.toString(); } /** * Get the string representation of the operator used to combine the two filters. This is used in the filter name. * * @return The operator */ protected abstract String getOperator(); /** * Filter the result using filter1 * * @param peak * The result * @return The filter result */ public boolean accept1(PeakResult peak) { return filter1.accept(peak); } /** * Filter the result using filter2 * * @param peak * The result * @return The filter result */ public boolean accept2(PeakResult peak) { return filter2.accept(peak); } /** * Filter the result using filter1 if it is a DirectFilter, otherwise return true * * @param peak * The result * @return The filter result, or true */ public boolean accept1(PreprocessedPeakResult peak) { result1 = (dfilter1 != null) ? dfilter1.validate(peak) : 0; return result1 == 0; } /** * Filter the result using filter2 if it is a DirectFilter, otherwise return true * * @param peak * The result * @return The filter result, or true */ public boolean accept2(PreprocessedPeakResult peak) { result2 = (dfilter2 != null) ? dfilter2.validate(peak) : 0; return result2 == 0; } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#setup(gdsc.smlm.results.MemoryPeakResults) */ @Override public void setup(MemoryPeakResults peakResults) { filter1.setup(peakResults); filter2.setup(peakResults); } @Override public void setup() { if (dfilter1 != null) dfilter1.setup(); if (dfilter2 != null) dfilter2.setup(); } @Override public void setup(int flags) { if (dfilter1 != null) dfilter1.setup(flags); if (dfilter2 != null) dfilter2.setup(flags); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#end() */ @Override public void end() { filter1.end(); filter2.end(); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#getNumericalValue() */ @Override public double getNumericalValue() { return filter1.getNumericalValue(); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#getNumericalValueName() */ @Override public String getNumericalValueName() { return filter1.getNumericalValueName(); } @Override public int getNumberOfParameters() { return filter1.getNumberOfParameters() + filter2.getNumberOfParameters(); } @Override protected double getParameterValueInternal(int index) { if (index < filter1.getNumberOfParameters()) return filter1.getParameterValueInternal(index); return filter2.getParameterValueInternal(index - filter1.getNumberOfParameters()); } @Override public double getParameterIncrement(int index) { checkIndex(index); if (index < filter1.getNumberOfParameters()) return filter1.getParameterIncrement(index); return filter2.getParameterIncrement(index - filter1.getNumberOfParameters()); } @Override public double getDisabledParameterValue(int index) { checkIndex(index); if (index < filter1.getNumberOfParameters()) return filter1.getDisabledParameterValue(index); return filter2.getDisabledParameterValue(index - filter1.getNumberOfParameters()); } @Override public ParameterType getParameterType(int index) { checkIndex(index); if (index < filter1.getNumberOfParameters()) return filter1.getParameterType(index); return filter2.getParameterType(index - filter1.getNumberOfParameters()); } @Override public Filter adjustParameter(int index, double delta) { checkIndex(index); Filter f1 = filter1; Filter f2 = filter2; if (index < filter1.getNumberOfParameters()) f1 = filter1.adjustParameter(index, delta); else f2 = filter2.adjustParameter(index - filter1.getNumberOfParameters(), delta); return createFilter(f1, f2); } @Override public int lowerBoundOrientation(int index) { if (dfilter1 == null) return 0; if (index < dfilter1.getNumberOfParameters()) return dfilter1.lowerBoundOrientation(index); if (dfilter2 == null) return 0; return dfilter2.lowerBoundOrientation(index - dfilter1.getNumberOfParameters()); } /** * Create a new combined filter from the two input filters * * @param f1 * @param f2 * @return */ protected abstract Filter createFilter(Filter f1, Filter f2); /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#create(double[]) */ @Override public Filter create(double... parameters) { double[] p1 = Arrays.copyOf(parameters, filter1.getNumberOfParameters()); double[] p2 = Arrays.copyOfRange(parameters, filter1.getNumberOfParameters(), parameters.length); return createFilter(filter1.create(p1), filter2.create(p2)); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#weakestParameters(double[]) */ @Override public void weakestParameters(double[] parameters) { double[] p1 = Arrays.copyOf(parameters, filter1.getNumberOfParameters()); double[] p2 = Arrays.copyOfRange(parameters, filter1.getNumberOfParameters(), parameters.length); filter1.weakestParameters(p1); filter2.weakestParameters(p2); System.arraycopy(p1, 0, parameters, 0, p1.length); System.arraycopy(p2, 0, parameters, p1.length, p2.length); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#subsetWithFailCount() */ @Override public boolean subsetWithFailCount() { return filter1.subsetWithFailCount() && filter2.subsetWithFailCount(); } /* * (non-Javadoc) * * @see gdsc.smlm.ga.Chromosome#length() */ public int length() { return filter1.length() + filter2.length(); } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#lowerLimit() */ @Override public double[] lowerLimit() { double[] l1 = filter1.lowerLimit(); double[] l2 = filter2.lowerLimit(); if (l1 == null && l2 == null) return null; return combine(getLowerLimit(filter1, l1), getLowerLimit(filter2, l2)); } private double[] getLowerLimit(Filter filter, double[] lower) { if (lower == null) { // Default to zero on the lower so no need to fill lower = new double[filter.getNumberOfParameters()]; } return lower; } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#upperLimit() */ @Override public double[] upperLimit() { double[] u1 = filter1.upperLimit(); double[] u2 = filter2.upperLimit(); if (u1 == null && u2 == null) return null; return combine(getUpperLimit(filter1, u1), getUpperLimit(filter2, u2)); } private double[] getUpperLimit(Filter filter, double[] upper) { if (upper == null) { upper = new double[filter.getNumberOfParameters()]; Arrays.fill(upper, Double.POSITIVE_INFINITY); } return upper; } /* * (non-Javadoc) * * @see gdsc.smlm.ga.Chromosome#sequence() */ public double[] sequence() { return combine(filter1.sequence(), filter2.sequence()); } /* * (non-Javadoc) * * @see gdsc.smlm.ga.Chromosome#mutationStepRange() */ public double[] mutationStepRange() { return combine(filter1.mutationStepRange(), filter2.mutationStepRange()); } private static double[] combine(double[] s1, double[] s2) { double[] s = new double[s1.length + s2.length]; System.arraycopy(s1, 0, s, 0, s1.length); System.arraycopy(s2, 0, s, s1.length, s2.length); return s; } /* * (non-Javadoc) * * @see gdsc.smlm.results.filter.Filter#getChromosomeParameters() */ public int[] getChromosomeParameters() { int[] s1 = filter1.getChromosomeParameters(); int[] s2 = filter2.getChromosomeParameters(); int[] s = new int[s1.length + s2.length]; System.arraycopy(s1, 0, s, 0, s1.length); // Copy the next array but offset the index by the number of parameters in filter 1 // so that getParameterName(int) works OK final int n1 = filter1.getNumberOfParameters(); for (int i = 0, j = s1.length; i < s2.length; i++, j++) s[j] = s2[i] + n1; return s; } }