package stoppers; import java.lang.*; import java.io.*; import main.BetterTokenizer; import main.Cell; import main.GeneralInput; import main.Model; public class OscillatingStripeStop extends SimpleStop { float StripeScore=0f; float currentStripeScore=0f; boolean avgstripescore=false; String node; int pos=0; // matches array index boolean lookForPeak=true; int navsteps=0; int maxavsteps=4; boolean averaging=false; boolean done=false; // oscillation tracking float thisval=-1f, lastval=-1f; float currentPeak=-1f, oldPeak=-1f, currentTrough=2f, oldTrough=2f; float thisAmp=-1f, oldAmp=-1f; float threshold=0; float interval=1f; int per=0; float alpha=0.2f; // stripe parameters float sMin=0.2f; // higher is better float sDif=5f; // higher is better float sVar=2f; // lower is better // No arg constructor for instantiating by name public OscillatingStripeStop() { stopTime = 1; } public OscillatingStripeStop(float stop_time) { stopTime = stop_time; } public boolean stop(float time) { // trivial reasons to return if(time < lastTime + interval) { return false; } if(done) return true; // can't remember why I put this in // MODIFIED 5/28 GvonD // KLUDGE TO STOP IT FROM GETTING STUCK // this is important - there are several ways this oscillation tracking scheme // can fail to "catch" and this bit handles them and makes sure the stopper // returns at least something relevant. It ought usually to be the case that // this handler is entered only when things are truly stable, but it will also // catch chaotic behavior (which to date has not been seen to occur) if((!averaging && (time > stopTime)) || (time > 2*stopTime)) { // MODIFIED 1/12 GvonD // This if statement used to be entered only when per<2, but the case where per>=2 is troublesome if(true) { // need to handle this situation far more gracefully thisval = model.cells[pos].nodePeek(node); if(!avgstripescore) { StripeScore = scoreStripe(model.cells,time); } score = (float)Math.pow((double)alpha,(double)StripeScore); return done=true; } else averaging = true; // what are we REALLY supposed to do here? // this line causes a problem; if something is steadily declining but // has already made a few peaks, or the other way around, this statement // allows the thing to keep running "forever". At present this line is // not reachable. // END OF MODIFICATION } // END OF KLUDGE // END OF MODIFICATION if(avgstripescore) { currentStripeScore = scoreStripe(model.cells, time); } thisval = model.cells[pos].nodePeek(node); if(lookForPeak) { // don't do anything if we're on our way up... if(thisval <= lastval) { // ...but if we're on our way down, oldPeak = currentPeak; currentPeak = lastval; // ...record the peak, if(!avgstripescore) { StripeScore = currentStripeScore = scoreStripe(model.cells, time); } lookForPeak = false; // ...and start looking for a trough } } else { // don't do anything if we're on our way down... if(thisval >= lastval) { // ...but if we've turned up, per++; oldTrough = currentTrough; currentTrough = lastval; // ...record the trough, lookForPeak = true; // ...set to look for a peak next time, oldAmp = thisAmp; // ...and update the amplitudes thisAmp = currentPeak - currentTrough; //System.out.println("\t" + thisAmp + "\t" + currentPeak + "\tscore:\t" + score); if(averaging) { score = navsteps*score + (float)Math.pow((double)(alpha+(1-alpha)*thisAmp/currentPeak),(double)StripeScore); score /= (++navsteps); if(navsteps == maxavsteps) { //System.out.println(score); return done=true; } } else if(oldAmp != -1) { // if we've gotten at least two amplitudes... if(Math.abs(thisAmp - oldAmp) < threshold * currentPeak) { // ...and the amplitude is changing slowly, score = (float)Math.pow((double)(alpha+(1-alpha)*thisAmp/currentPeak),(double)StripeScore); averaging = true; navsteps = 1; } } } } lastval = thisval; lastTime = time; return false; } public float scoreStripe(Cell [] cells, float time) { float temp=0f; float [] left = new float[model.arrayHeight]; float [] center = new float[model.arrayHeight]; float [] right = new float[model.arrayHeight]; float leftMean = 0f, centerMean = 0f, rightMean = 0f; float leftMax = 0f, centerMin = 1f, centerMax = 0f, rightMax = 0f; for(int i = 0; i < model.arrayHeight; i++) { left[i] = cells[pos+(i*model.arrayWidth)].neighbors[5].nodePeek(node); if(leftMax < left[i]) leftMax = left[i]; leftMean += left[i]; center[i] = cells[pos+(i*model.arrayWidth)].nodePeek(node); if(centerMin > center[i]) centerMin = center[i]; if(centerMax < center[i]) centerMax = center[i]; centerMean += center[i]; right[i] = cells[pos+(i*model.arrayWidth)].neighbors[2].nodePeek(node); if(rightMax < right[i]) rightMax = right[i]; rightMean += right[i]; } leftMean /= model.arrayHeight; centerMean /= model.arrayHeight; rightMean /= model.arrayHeight; temp = 2 * (centerMean/(sMin+centerMean)); // note that if sMin is high (i.e., near 1), the stripe score can't ever have much impact if(centerMax > 0) { temp *= 2 * (centerMin/((centerMax/sVar)+centerMin)); if(rightMean+leftMean > 0.00001) { temp *= 2 * ((centerMean/((rightMean+leftMean)/2))/(sDif+(centerMean/((rightMean+leftMean)/2)))); } else temp *= 2; temp *= (1 + (centerMin - (leftMax+rightMax)/2)); } else temp = 0; // now merge the momentary score into the running average if(avgstripescore) { StripeScore *= lastTime; StripeScore += temp*(time-lastTime); StripeScore /= time; } return temp; } public void reset() { super.reset(); StripeScore=0f; thisval=-1f; lastval=-1f; currentPeak=-1f; oldPeak=-1f; currentTrough=2f; oldTrough=2f; thisAmp=-1f; oldAmp=-1f; lookForPeak=true; lastTime=0f; per = 0; averaging = false; done = false; navsteps = 0; } protected void loadParameter(String info, BetterTokenizer tokenizer) throws Exception { if(info.equals("Node")) { GeneralInput.nextToken(tokenizer); node = tokenizer.sval; } else if(info.equals("Position")) { GeneralInput.nextToken(tokenizer); pos = ((int)tokenizer.nval-1); } // subtract 1 to make pos an array index else if(info.equals("StabilityThreshold")) { GeneralInput.nextToken(tokenizer); threshold = (float)tokenizer.nval; } else if(info.equals("Interval")) { GeneralInput.nextToken(tokenizer); interval = (float)tokenizer.nval; } else if(info.equals("Alpha")) { GeneralInput.nextToken(tokenizer); alpha = (float)tokenizer.nval; } else if(info.equals("StripeThreshold")) { GeneralInput.nextToken(tokenizer); sMin = (float)tokenizer.nval; } else if(info.equals("StripeVariance")) { GeneralInput.nextToken(tokenizer); sVar = (float)tokenizer.nval; } else if(info.equals("StripeDifference")) { GeneralInput.nextToken(tokenizer); sDif = (float)tokenizer.nval; } else if(info.equals("AverageStripeScore")) { avgstripescore = true; } else super.loadParameter(info,tokenizer); } }