Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented feature req #174 (Dump/Restore state) #177

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/java/no/priv/garshol/duke/ConfigLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@ else if (localName.equals("comparator")) {
datasource = null;
currentobj = null;
} else if (localName.equals("object")) {
comparator = (Comparator) currentobj;
config.addCustomComparator(comparator);
if (currentobj instanceof Comparator)
// store custom comparators so genetic algorithm can get them
config.addCustomComparator((Comparator) currentobj);
currentobj = null;
}
else if (localName.equals("database"))
Expand Down
29 changes: 26 additions & 3 deletions src/main/java/no/priv/garshol/duke/genetic/Driver.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ public class Driver {
public static void main(String[] argv) throws IOException, SAXException {
// parse command-line
CommandLineParser parser = new CommandLineParser();
parser.setMinimumArguments(1);
parser.setMinimumArguments(0);
parser.setMaximumArguments(1);
parser.addStringOption("testfile", 'T');
parser.addBooleanOption("scientific", 's');
parser.addStringOption("generations", 'G');
parser.addStringOption("population", 'P');
parser.addStringOption("questions", 'Q');
parser.addStringOption("output", 'O');
parser.addStringOption("dump-state", 'D');
parser.addStringOption("restore-state", 'R');
parser.addStringOption("threads", 't');
parser.addBooleanOption("active", 'A');
parser.addStringOption("linkfile", 'l');
Expand All @@ -48,23 +50,41 @@ public static void main(String[] argv) throws IOException, SAXException {
System.err.println("ERROR: scientific mode requires a test file");
System.exit(1);
}

String restoreStateDir = parser.getOptionValue("restore-state");
if (argv.length == 0 && restoreStateDir == null) {
System.err.println("ERROR: must specify a config file or be in restore-state mode");
System.exit(1);
}

if (argv.length == 1 && restoreStateDir != null) {
System.out.println("WARNING: cannot specify a config file and be in restore-state mode. Ignoring restore-state option");
}

// get started
Configuration config = ConfigLoader.load(argv[0]);
Configuration config;
if (parser.getOptionValue("restore-state") != null)
config = ConfigLoader.load(restoreStateDir + "//config_1.xml");
else
config = ConfigLoader.load(argv[0]);

GeneticAlgorithm genetic =
new GeneticAlgorithm(config, testfile,
parser.getOptionState("scientific"));
genetic.setPopulation(parser.getOptionInteger("population", 100));
genetic.setGenerations(parser.getOptionInteger("generations", 100));
genetic.setQuestions(parser.getOptionInteger("questions", 10));
genetic.setConfigOutput(parser.getOptionValue("output"));
genetic.setDumpStateDir(parser.getOptionValue("dump-state"));
genetic.setRestoreStateDir(parser.getOptionValue("restore-state"));
genetic.setThreads(parser.getOptionInteger("threads", 1));
genetic.setSparse(parser.getOptionState("sparse"));
genetic.setMutationRate(parser.getOptionInteger("mutation-rate", -1));
genetic.setRecombinationRate(parser.getOptionDouble("recombination-rate", -1.0));
genetic.setEvolveComparators(!parser.getOptionState("no-comparators"));
genetic.setCopiesOfOriginal(parser.getOptionInteger("original", 0));
genetic.setIncompleteTest(parser.getOptionState("incomplete-data"));
//Meaningless when restoring full population from saved state
genetic.setCopiesOfOriginal((restoreStateDir == null) ? parser.getOptionInteger("original", 0) : 0);
if (parser.getOptionState("active"))
genetic.setActive(true);
if (parser.getOptionValue("linkfile") != null)
Expand All @@ -84,6 +104,9 @@ private static void usage() {
System.out.println(" --sparse don't ask questions after every generation");
System.out.println(" --output=<file> file to write best configuration to");
System.out.println(" (a new export after every generation)");
System.out.println(" --dump-state=<dir> directory to dump the current state to");
System.out.println(" (dumps after every generation)");
System.out.println(" --restore-state=<dir> directory to load state from (restart from saved state)");
System.out.println(" --threads=N number of threads to run");
System.out.println(" --linkfile=<file> write user's answers to this file");
System.out.println(" --scientific test active learning");
Expand Down
54 changes: 51 additions & 3 deletions src/main/java/no/priv/garshol/duke/genetic/GeneticAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import java.util.Comparator;
import java.util.Collection;
import java.util.Collections;
import java.io.File;
import java.io.IOException;

import org.xml.sax.SAXException;

import no.priv.garshol.duke.Link;
import no.priv.garshol.duke.Record;
import no.priv.garshol.duke.Database;
Expand Down Expand Up @@ -43,6 +46,8 @@ public class GeneticAlgorithm {
private boolean scientific;
private Oracle oracle;
private String outfile; // file to write config to
private String dumpStateDir; // file to dump state to
private String restoreStateDir; // file to restore state from
private Map<GeneticConfiguration, Double> sciencetracker;
private boolean quiet; // limit output
private boolean incomplete; // is test file incomplete?
Expand Down Expand Up @@ -123,6 +128,21 @@ public void setConfigOutput(String output) {
this.outfile = output;
}

/**
* Set the directory to dump the state to. The full genetic
* population gets dumped at the end of each generation.
*/
public void setDumpStateDir(String dumpDir) {
this.dumpStateDir = dumpDir;
}

/**
* Set the directory to restore the state from.
*/
public void setRestoreStateDir(String restoreDir) {
this.restoreStateDir = restoreDir;
}

/**
* Sets the number of threads to run the genetic algorithm in.
*/
Expand Down Expand Up @@ -239,9 +259,19 @@ public void run() {
}
}

// make first, random population
population.create();

// make first population (either random, or restored from previously saved state)
if (restoreStateDir == null)
population.create();
else
try {
population.restore(restoreStateDir);
}
catch (IOException e) {
System.err.println("ERROR: Cannot read files from " + restoreStateDir + ": " + e);
} catch (SAXException e) {
System.err.println("ERROR: Cannot parse xml files from " + restoreStateDir + ": " + e);
}

// run through the required number of generations
double prevbest = 0.0;
int stuck_for = 0; // number of generations f has remained unchanged
Expand Down Expand Up @@ -337,6 +367,24 @@ else if (gen_no > 1)
}
}

// if asked to, dump state
if (dumpStateDir != null) {
String dumpPath = null;
try {
File d = new File(dumpStateDir);
d.mkdirs();
Configuration cfg;
for (int c = 0; c < population.size(); c++) {
cfg = population.getNthConfiguration(c).getConfiguration();
dumpPath = dumpStateDir + "//config_" + (c+1) + ".xml";
ConfigWriter.write(cfg, dumpPath);
}
cfg = null;
} catch (IOException e) {
System.err.println("ERROR: Cannot write to '" + dumpPath + "': " + e);
}
}

// is there any point in evolving?
if (active &&
population.getBestConfiguration().getFNumber() ==
Expand Down
41 changes: 39 additions & 2 deletions src/main/java/no/priv/garshol/duke/genetic/GeneticPopulation.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@

package no.priv.garshol.duke.genetic;

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;

import org.xml.sax.SAXException;

import no.priv.garshol.duke.Configuration;
import no.priv.garshol.duke.ConfigLoader;

/**
* Keeps track of the population.
Expand All @@ -29,7 +35,7 @@ public GeneticPopulation(Configuration config) {
}

/**
* Creates the initial population.
* Creates the initial random population.
*/
public void create() {
GeneticConfiguration cfg =
Expand All @@ -42,6 +48,30 @@ public void create() {
for (; ix < size; ix++)
population.add(cfg.makeRandomCopy());
}

/**
* Creates the initial population from a previously saved state
*/
public void restore(String dirStr) throws IOException, SAXException {
File dir = new File(dirStr);
File[] files = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.matches("config_\\d+\\.xml");
}
}
);

Configuration config;
GeneticConfiguration geneticConfig;
population = new ArrayList(files.length);

for (int ix = 0; ix < files.length; ix++) {
config = ConfigLoader.load(dir + "//config_" + (ix+1) + ".xml");
geneticConfig = new GeneticConfiguration(config, mutation_rate, recombination_rate,
evolve_comparators);
population.add(geneticConfig.makeCopy());
}
}

/**
* Returns all configurations in the current generation.
Expand Down Expand Up @@ -69,6 +99,13 @@ public void sort() {
public GeneticConfiguration getBestConfiguration() {
return population.get(0);
}

/**
* Returns the nth configuration.
*/
public GeneticConfiguration getNthConfiguration(int n) {
return population.get(n);
}

/**
* Returns the worst configuration.
Expand Down