package iterators; import java.io.*; import java.util.Calendar; import java.util.GregorianCalendar; import main.BetterTokenizer; import main.GeneralInput; import java.lang.*; import main.Model; import main.Cell; import main.Node; import affectors.Affector; import parameters.Parameter; import parameters.ParameterSet; import stoppers.NoChangeStop; import stoppers.SimpleStop; import main.MoreMath; import main.Globals; /** This performs a sensitivity analysis on a model. It takes each parameter and varies it across its whole range, in a user determined number of steps, while holding all the other parameters at their original values. At each parameter value, it runs the model and records the score from the stopper. These all are saved to a file, which at the end contains the scores from transects along each parameter emanating out from the original (presumably good) parameter set. This tells you something about how large the basin of good behavior is that the original parameter set is sitting in, and how robust that parameter set would be to small changes in parameters. Note that you can easily imagine cases where this analysis would fool you as to the size or shape of the basin. */ public class TransectIterator extends ModelIterator implements Runnable { int stepNum=0; int numSteps=100; float [] values; float [] scores; int paramSetCount=0; PrintWriter localOut=null; float [] ranges; String outfileName; float threshold; public TransectIterator() { } // Have a no argument initializer so we can do new_by_name public ModelIterator copy() throws Exception { TransectIterator newIter = new TransectIterator(); newIter.init(this.network, this.model); newIter.nParsTV = this.nParsTV; newIter.parsTV = this.parsTV.copy(); newIter.theFunction = this.theFunction.copy(); newIter.values = new float[numSteps + 1]; System.arraycopy(values, 0, newIter.values, 0, numSteps + 1); newIter.scores = new float[numSteps + 1]; System.arraycopy(scores, 0, newIter.scores, 0, numSteps + 1); newIter.ranges = new float[nParsTV]; System.arraycopy(ranges, 0, newIter.ranges, 0, nParsTV); return newIter; } public void saveOutputTags(PrintWriter ps, String inset) { super.saveOutputTags(ps, inset); for(int i = 0; i < nParsTV; i++) { ps.println(parsTV.getName(i) + "_Range\tnumber"); } } public void saveOutput(PrintWriter ps, String inset) { super.saveOutput(ps, inset); for(int i = 0; i < nParsTV; i++) { ps.println(parsTV.getName(i) + "_Range\t" + ranges[i]); } } public void loadParameters(BetterTokenizer tokenizer) throws Exception { super.loadParameters(tokenizer); // The grunt work is done by parent class // This is initialization stuff we need to do after figuring out how many // parameters will be varied. values = new float[numSteps + 1]; scores = new float[numSteps + 1]; ranges = new float[parsTV.getNumParams()]; Calendar calendar = new GregorianCalendar(); outfileName = outfileName + "_" + (calendar.get(Calendar.MONTH) + 1) + "_" + calendar.get(Calendar.DAY_OF_MONTH) + "_" + calendar.get(Calendar.HOUR_OF_DAY) + "_" + calendar.get(Calendar.MINUTE) + "_" + calendar.get(Calendar.SECOND); try { localOut = new PrintWriter(new FileOutputStream(outFileName), true); } catch(IOException e) { throw new Exception ("Error creating TransectIterator output file: " + e.toString()); } } // Put any parameters specific to this iterator class in here, as if clauses. protected void loadParameter(String info, BetterTokenizer tokenizer) throws Exception { if(info.equals("Threshold")) {GeneralInput.nextToken(tokenizer); threshold = (float)tokenizer.nval; } else if(info.equals("NumSteps")) {GeneralInput.nextToken(tokenizer); numSteps = (int)tokenizer.nval; } else if(info.equals("TransectFile")) {GeneralInput.nextToken(tokenizer); outfileName = tokenizer.sval; } else super.loadParameter(info, tokenizer); } public void doRun() { int par_num; float low, high, incr, orig_value, closest_dist, val; int i, closest; reset(); for(par_num = 0; par_num < nParsTV; par_num++) { System.out.println("Doing par num " + par_num); low = parsTV.getLowerBound(par_num); high = parsTV.getUpperBound(par_num); if(parsTV.getVariationMode(par_num) == Parameter.LOGARITHMIC) { low = (float)Math.log(low); high = (float)Math.log(high); } incr = (high - low) / numSteps; orig_value = p[par_num]; closest_dist = 1000000; closest = -1; for(i = 0, val=low; i <= numSteps; i++, val+= incr) { if(parsTV.getVariationMode(par_num) == Parameter.LOGARITHMIC) p[par_num] = (float)Math.exp(val); else p[par_num] = val; values[i] = p[par_num]; scores[i] = F(p); // Save for later if(Math.abs(orig_value - values[i]) < closest_dist) { closest_dist = Math.abs(orig_value - values[i]) ; closest = i; } } p[par_num] = orig_value; // Save to special output file localOut.print(parsTV.getName(par_num) + "\t"); for(i = 0; i <= numSteps; i++) { localOut.print(values[i] + "\t"); } localOut.println(); localOut.print(parsTV.getName(par_num) + "\t"); for(i = 0; i <= numSteps; i++) { localOut.print(scores[i] + "\t"); } localOut.println(); // Figure out range of good values i = closest; while(i < numSteps + 1 && scores[i] < threshold) i++; if(i > 0) { high = values[i-1]; // Slightly dangerous - if the closest one is no good, this will be screwy i = closest; while(i >= 0 && scores[i] < threshold) i--; if(i < numSteps) { ranges[par_num] = high - values[i + 1]; } else ranges[par_num] = 0; } else ranges[par_num] = 0; } // Leave model in its original state - p will be set with orig values now parsTV.setFrom(p); parsTV.setModel(); finalScore = 0; super.stopRun(); } }