package bsearch.test;
//TODO: Improve test coverage! (It's pretty weak right now... :-/)
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.nlogo.util.MersenneTwisterFast;
import org.xml.sax.SAXException;
import bsearch.algorithms.SearchMethod;
import bsearch.algorithms.SearchMethodLoader;
import bsearch.algorithms.SearchParameterException;
import bsearch.app.BehaviorSearch;
import bsearch.app.BehaviorSearchException;
import bsearch.app.SearchProtocol;
import bsearch.app.BehaviorSearch.RunOptions;
import bsearch.nlogolink.ModelRunner;
import bsearch.nlogolink.NetLogoLinkException;
import bsearch.representations.Chromosome;
import bsearch.representations.ChromosomeFactory;
import bsearch.representations.ChromosomeTypeLoader;
import bsearch.representations.GrayBinaryChromosome;
import bsearch.representations.StandardBinaryChromosome;
import bsearch.space.SearchSpace;
import bsearch.util.GeneralUtils;
public strictfp class TestBehaviorSearch
{
public static final String PATH_TO_NETLOGO_MODELS = "/home/forrest/apps/netlogo/models/Sample Models/";
// a main method to run it -- for convenience.
public static void main( String... args )
{
org.junit.runner.JUnitCore.main
( TestBehaviorSearch.class.getName() ) ;
}
@Test
public void testModelRunner1() throws Exception
{
ModelRunner runner;
LinkedHashMap<String,Object> params;
runner = bsearch.nlogolink.ModelRunner.createModelRunnerForTesting(PATH_TO_NETLOGO_MODELS + "Earth Science/Fire.nlogo", true, 100);
runner.setSetupCommands( "setup" );
runner.setStepCommands( "go" );
runner.setStopConditionReporter( "burned-trees > 3000" );
runner.setMeasureIfReporter("ticks mod 10 = 0 or ticks = 37");
runner.addResultReporter( "burned-trees" );
params = new LinkedHashMap<String, Object>();
params.put("density", 61.0);
ModelRunner.RunSetup runSetup = new ModelRunner.RunSetup(0, params);
List<Double> results = runner.doFullRun( runSetup ).getTimeSeriesForMeasure("burned-trees");
Assert.assertEquals(5, results.size()); // 5 because we only get to tick 37, due to the stop condition.
Assert.assertEquals(3002.0, results.get(results.size() - 1));
Assert.assertEquals(37.0, runner.report( "ticks" ));
runner.command("go");
Assert.assertEquals(3068.0, runner.measureResults().get("burned-trees"));
runner.dispose();
}
@Test
public void testModelRunner2() throws Exception
{
ModelRunner runner;
LinkedHashMap<String,Object> params;
runner = bsearch.nlogolink.ModelRunner.createModelRunnerForTesting(PATH_TO_NETLOGO_MODELS + "Earth Science/Fire.nlogo", false, 100);
runner.setSetupCommands( "setup" );
runner.setStepCommands( "go" );
runner.setStopConditionReporter( "burned-trees > 3000" );
runner.setMeasureIfReporter("true");
runner.addResultReporter( "burned-trees" );
params = new LinkedHashMap<String, Object>();
params.put("density", 61.0);
// now do it manually, without using doFullRun()
runner.setup(0, params);
int i;
for (i = 0; i < 100; i++)
{
if (runner.go())
break;
}
Assert.assertEquals(3002.0, runner.measureResults().get("burned-trees"));
Assert.assertEquals(37.0, runner.report("ticks"));
runner.dispose();
}
@Test
public void testConstraintsTextGeneration() throws BehaviorSearchException, NetLogoLinkException
{
Assert.assertEquals(bsearch.nlogolink.Utils.getDefaultConstraintsText(PATH_TO_NETLOGO_MODELS + "/Social Science/Ethnocentrism.nlogo").trim(),
"[\"mutation-rate\" [0 0.001 1]]\n[\"death-rate\" [0 0.05 1]]\n[\"immigrants-per-day\" [0 1 100]]\n[\"initial-ptr\" [0 0.01 1]]\n[\"cost-of-giving\" [0 0.01 1]]\n[\"gain-of-receiving\" [0 0.01 1]]\n[\"immigrant-chance-cooperate-with-same\" [0 0.01 1]]\n[\"immigrant-chance-cooperate-with-different\" [0 0.01 1]]");
}
@Test
public void testSearchProtocol() throws IOException , SAXException
{
String FILENAME = GeneralUtils.attemptResolvePathFromBSearchRoot("test/TestProtocol.xml") ;
SearchProtocol sp = SearchProtocol.loadFile( FILENAME ) ;
java.io.StringWriter sw = new java.io.StringWriter() ;
sp.save( new PrintWriter( sw ) ) ;
// is the result the same as the original, ignoring all whitespace?
String result = sw.getBuffer().toString().replaceAll( "\\s" , "" ) ;
String original = bsearch.util.GeneralUtils.stringContentsOfFile( new java.io.File(FILENAME) ).replaceAll( "\\s" , "" ) ;
//System.out.println( result ) ;
//System.out.println( original ) ;
//System.out.println( result.equals( original ) ) ;
Assert.assertEquals(result, original);
}
@Test
public void testSearchMethodLoader() throws BehaviorSearchException
{
// just make sure there are no errors...
for (String s: SearchMethodLoader.getAllSearchMethodNames())
{
SearchMethod searcher = SearchMethodLoader.createFromName(s);
searcher.getSearchParams().toString();
}
}
@Test
public void testChromosomes() throws BehaviorSearchException
{
SearchSpace ss = new SearchSpace(Arrays.asList("[\"discrete1to4\" [1 1 4]]",
"[\"continuous0to1.5\" [0.0 \"C\" 1.5]]",
"[\"categorical\" \"apple\" \"banana\" \"cherry\"]",
"[\"const\" 25]",
"[\"discretedecimal\" [-1 0.17 2]]"));
MersenneTwisterFast rng = new MersenneTwisterFast();
for (String chromoType: ChromosomeTypeLoader.getAllChromosomeTypes())
{
ChromosomeFactory factory = ChromosomeTypeLoader.createFromName(chromoType);
Chromosome cs[] = new Chromosome[] {factory.createChromosome(ss, rng), factory.createChromosome(ss, rng)};
for (int i = 0; i < 1000; i++)
{
cs[0] = cs[0].mutate(0.20, rng);
cs[1] = cs[1].crossoverWith(cs[0], rng)[0];
for (Chromosome c: cs )
{
LinkedHashMap<String, Object> params = c.getParamSettings();
double val1 = (Double)params.get("discrete1to4");
double val2 = (Double)params.get("continuous0to1.5");
String val3 = (String) params.get("categorical");
Object val4 = params.get("const");
double val5 = (Double)params.get("discretedecimal");
Assert.assertTrue(chromoType + " check val1 >= 1", val1 >= 1 );
Assert.assertTrue(chromoType + " check val1 <= 4", val1 <= 4 );
Assert.assertTrue(chromoType + " check val2 >= 0.0", val2 >= 0.0 );
Assert.assertTrue(chromoType + " check val2 <= 1.5", val2 <= 1.5 );
Assert.assertTrue(chromoType + " check val3 okay", val3.equals("apple") || val3.equals("banana") || val3.equals("cherry"));
Assert.assertTrue(chromoType + " check val4 okay", val4.equals(new Double(25)));
Assert.assertTrue(chromoType + " check val5 >= -1", val5 >= -1 );
Assert.assertTrue(chromoType + " check val5 <= 2", val5 <= 2 );
}
}
for (int i = 0; i < 10000; i++)
{
Chromosome c = factory.createChromosome(ss, rng);
//System.out.println(chromoType + " i=" + i + " : " + c);
Chromosome c2 = factory.createChromosome(ss, c.getParamSettings());
Assert.assertEquals(chromoType + " check recreate Chromosomes from values:", c.getParamSettings(), c2.getParamSettings());
}
}
}
@Test
public void testBinaryConversion()
{
int numBits = 8;
boolean[] bits = new boolean[numBits + 12];
SearchSpace ss = new SearchSpace(Arrays.asList("[\"moo\" [1 1 4]]"));
GrayBinaryChromosome bcgray = new GrayBinaryChromosome(ss, new MersenneTwisterFast() );
StandardBinaryChromosome bcstd = new StandardBinaryChromosome(ss, new MersenneTwisterFast() );
for (long i = 0; i < (1L << numBits); i ++)
{
bcgray.binaryEncode(i, bits, 5, numBits);
long dec = bcgray.binaryDecode(bits, 5, numBits);
Assert.assertEquals(i, dec);
bcstd.binaryEncode(i, bits, 5, numBits);
dec = bcstd.binaryDecode(bits, 5, numBits);
// if (i != dec)
// {
// System.out.println("i = " + i + ": " + BinaryChromosome.toBinaryString(bits) + " decode=" + dec);
// }
Assert.assertEquals(i, dec);
}
numBits = 62 ;
bits= new boolean[numBits + 3];
for (long i = 0; i < (1L << numBits); i += 15037093017331L)
{
bcgray.binaryEncode(i, bits, 2, numBits);
long dec = bcgray.binaryDecode(bits, 2, numBits);
Assert.assertEquals(i, dec);
bcstd.binaryEncode(i, bits, 2, numBits);
dec = bcstd.binaryDecode(bits, 2, numBits);
Assert.assertEquals(i, dec);
}
}
public static <T> String join(final Iterable<T> objs, final String delimiter) {
java.util.Iterator<T> iter = objs.iterator();
if (!iter.hasNext())
return "";
StringBuffer buffer = new StringBuffer(String.valueOf(iter.next()));
while (iter.hasNext())
buffer.append(delimiter).append(String.valueOf(iter.next()));
return buffer.toString();
}
@Test
public void testConsistentOutputResults() throws IOException , SAXException, SearchParameterException, BehaviorSearchException, InterruptedException, CmdLineException
{
LinkedHashMap<String,String> scenarios = new LinkedHashMap<String,String>();
scenarios.put("TesterSuperRandom","-p test/TesterSuperRandom.bsearch -o test/tmp/TesterSuperRandom -t 7 -n 2 --randomseed 123 --quiet");
scenarios.put("Tester1","-p test/Tester.bsearch -o test/tmp/Tester1 -t 1 -n 2 -f 3 --randomseed 1234 --quiet");
scenarios.put("Tester2","-p test/Tester.bsearch -o test/tmp/Tester2 -t 2 -n 3 -f 10 --randomseed 99 --quiet");
scenarios.put("TesterNoisy","-p test/TesterNoisy.bsearch -o test/tmp/TesterNoisy -t 2 -n 2 --randomseed 123 --quiet");
scenarios.put("TesterGANoCache","-p test/TesterGANoCache.bsearch -o test/tmp/TesterGANoCache -n 1 -t 2 --randomseed 99 --quiet");
scenarios.put("TesterSA_Deriv","-p test/TesterSA_Deriv.bsearch -o test/tmp/TesterSA_Deriv -n 2 -t 1 --randomseed 67 --quiet");
scenarios.put("TesterNoisy_RS","-p test/TesterNoisy_RS.bsearch -o test/tmp/TesterNoisy_RS -t 1 -n 1 --randomseed 123 --quiet");
scenarios.put("TesterNoisy_RS","-p test/TesterNoisy_RS.bsearch -o test/tmp/TesterNoisy_RS -t 5 -n 1 --randomseed 123 --quiet");
List<String> outputExtensions = Arrays.asList(".searchConfig.xml", ".modelRunHistory.csv",
".objectiveFunctionHistory.csv", ".bestHistory.csv", ".finalBests.csv", ".finalCheckedBests.csv");
List<String> failedList = new ArrayList<String>();
for (String key: scenarios.keySet())
{
//System.out.println("key: + " + key);
RunOptions clOptions = new RunOptions();
CmdLineParser parser = new CmdLineParser(clOptions);
parser.parseArgument(scenarios.get(key).split("\\s"));
BehaviorSearch.runWithOptions(clOptions);
for (String extension: outputExtensions)
{
String checkFileName = GeneralUtils.attemptResolvePathFromBSearchRoot("test/checks/" + key + extension);
String testFileName = GeneralUtils.attemptResolvePathFromBSearchRoot("test/tmp/" + key + extension);
File checkFile = new File(checkFileName);
File testFile = new File(testFileName);
String expected = checkFile.exists()?GeneralUtils.stringContentsOfFile(checkFile):"*no_file*";
String result = testFile.exists()?GeneralUtils.stringContentsOfFile(testFile):"*no_file*";
if (!result.equals(expected))
{
failedList.add(key + extension);
}
}
}
if (!failedList.isEmpty())
{
Assert.fail("Output files that differ: " + join(failedList, ", "));
}
}
}