diff options
Diffstat (limited to 'src/genetic/ParallelChessGeneticProcess.java')
| -rw-r--r-- | src/genetic/ParallelChessGeneticProcess.java | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/src/genetic/ParallelChessGeneticProcess.java b/src/genetic/ParallelChessGeneticProcess.java new file mode 100644 index 0000000..75d9425 --- /dev/null +++ b/src/genetic/ParallelChessGeneticProcess.java @@ -0,0 +1,267 @@ +/* + Copyright 2007 Scott Gasch + + Licensed under the Apache License, Version 2.0 (the "License"); you + may not use this file except in compliance with the License. You may + obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. +*/ + +package org.guru.wannabe.genetic; + +import java.io.*; +import java.util.regex.*; + +import java.util.Map; +import java.util.TreeMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +public class ParallelChessGeneticProcess implements GeneticProcess { + private static final int numProcessors = 4; + private static final String baselineDna = "./baseline.dna"; + private static final String baselineLog = "./baseline.log"; + private static final String scriptName = "./trial.ep_"; + + private int numGenerations; + private int numPerGeneration; + private int numSeeds; + + // Helper function to emulate system(3). + private static void system(String cmd[]) { + Runtime runtime = Runtime.getRuntime(); + try { + Process ps = runtime.exec(cmd); + InputStream stdin = ps.getInputStream(); + InputStreamReader isr = new InputStreamReader(stdin); + BufferedReader br = new BufferedReader(isr); + String line = null; + while((line = br.readLine()) != null) ; + ps.waitFor(); + } catch(Exception e) { + throw new AssertionError(e); + } + } + + // Private helpers to build filenames + private static String seedFilename(int generation, int which) { + return "./gen" + generation + "_seed" + which + ".dna"; + } + + private static String logFilename(int generation, int which) { + return "./gen" + generation + "_" + which + ".log"; + } + + private static String dnaFilename(int generation, int which) { + return "./gen" + generation + "_" + which + ".dna"; + } + + // Uses mutate.pl to cause dna mutations + private static void mutate(String from, String to) { + String cmd[] = new String[] { "./mutate.pl", from, to }; + system(cmd); + try { + BufferedWriter out = + new BufferedWriter(new FileWriter(to, true)); + out.write("# mutate " + from + " -> " + to + "\n"); + out.close(); + } catch(IOException e) { + throw new AssertionError(e); + } + } + + // Runs a chess trial event + private static void chessTrial(String dnaFile, String logFile) { + String cmd[] = new String[] { "./typhoon", + "--cpus", "1", + "--hash", "128m", + "--batch", + "--logfile", logFile, + "--dnafile", dnaFile, + "--command", "script ./trial.ep_" }; + system(cmd); + } + + // The mainInitialize method produces the 0th generation DNA from + // a seed file called "baseline.dna" using a helper script called + // mutate.pl (the generic DNA mutation routine). + public boolean mainInitialize(int numGenerations, + int numPerGeneration) { + + System.out.println("Performing pre-generation setup tasks."); + this.numGenerations = numGenerations; + this.numPerGeneration = numPerGeneration; + this.numSeeds = numPerGeneration / 5; + if ((numGenerations <= 0) || + (numPerGeneration <= 0) || + (numSeeds <= 0)) { + throw new IllegalArgumentException("Bad value"); + } + + // Create generation zero seeds based on mutations of baseline.dna + for (int x = 0; x < numSeeds; x++) { + String seedFile = seedFilename(0, x); + if (!new File(seedFile).exists()) { + mutate(baselineDna, seedFile); + } else { + System.out.println("Using existing " + seedFile); + } + } + + // Run baseline.dna trial to get a starting point. + if (!new File(baselineLog).exists()) { + chessTrial(baselineDna, baselineLog); + } else { + System.out.println("Using existing " + baselineLog); + } + return true; + } + + public boolean generationInitialize(int generationNumber) { + + // Using the seed dna of the previous generation, build the + // genes for the current generation. + System.out.println("Initializing generation " + generationNumber); + for (int x = 0; x < numPerGeneration; x++) { + String dnaFile = dnaFilename(generationNumber, x); + String seedFile = seedFilename(generationNumber, x % numSeeds); + mutate(seedFile, dnaFile); + } + return true; + } + + public boolean generationEvaluation(int generationNumber) { + class ChessGame implements Runnable { + public String dnaFile; + public String logFile; + public ChessGame(String dnaFile, String logFile) { + this.dnaFile = dnaFile; this.logFile = logFile; + } + public void run() { + if (!new File(logFile).exists()) { + System.out.println("Creating " + logFile); + chessTrial(dnaFile, logFile); + } else { + System.out.println("Using existing " + logFile); + } + } + } + + // Populate work queue + System.out.println("Evaluating generation " + generationNumber); + List<ChessGame> workQueue = new ArrayList<ChessGame>(); + for (int x = 0; x < numPerGeneration; x++) { + String dnaFile = dnaFilename(generationNumber, x); + String logFile = logFilename(generationNumber, x); + workQueue.add(new ChessGame(dnaFile, logFile)); + } + + // Depopulate the work queue in parallel + Thread threadPool[] = new Thread[numProcessors]; + while(!workQueue.isEmpty()) { + ChessGame next = workQueue.remove(0); + int x = 0; + do { + if ((threadPool[x] == null) || + (!threadPool[x].isAlive())) { + threadPool[x] = new Thread(next); + threadPool[x].setPriority(Thread.MAX_PRIORITY); + threadPool[x].start(); + break; + } + x++; + if (x >= numProcessors) { + x = 0; + try { + Thread.sleep(2000); + } catch(Exception e) { + ; + } + } + } + while(true); + } + + // Wait for all threads to become idle again + int x = 0; + do { + if (threadPool[x] != null && threadPool[x].isAlive()) { + x = 0; + } + x++; + try { + Thread.sleep(2000); + } catch(Exception e) { + ; + } + } while(x < numProcessors); + return true; + } + + public boolean generationReproduction(int generationNumber) { + + // Helper data class + class FileAndScore implements Comparable<FileAndScore> { + public final String file; + public final int score; + public final int number; + public FileAndScore(String file, int score, int number) { + this.file = file; this.score = score; this.number = number; + } + public int compareTo(FileAndScore other) { + return other.score - score; + } + } + + System.out.println("Seeding subsequent generation."); + List<FileAndScore> scores = new ArrayList<FileAndScore>(); + for (int x = 0; x < numPerGeneration; x++) { + String logFile = logFilename(generationNumber, x); + try { + BufferedReader log = new BufferedReader( + new FileReader(logFile)); + String line = null; + Pattern p = + Pattern.compile("\\s+correct solutions\\s+:\\s+(\\d+)"); + while ((line = log.readLine()) != null) { + Matcher m = p.matcher(line); + if (m.matches()) { + int score = Integer.parseInt(m.group(1)); + FileAndScore fas = + new FileAndScore(logFile, score, x); + scores.add(fas); + break; + } + } + log.close(); + } + catch (Exception e) { + throw new AssertionError(e); + } + } + java.util.Collections.sort(scores); + for (int x = 0; x < numSeeds; x++) { + FileAndScore breeder = scores.get(0); + scores.remove(0); + String dnaFile = dnaFilename(generationNumber, breeder.number); + String seedFile = seedFilename(generationNumber + 1, x); + if (!new File(seedFile).exists()) { + System.out.println("Number " + breeder.number + " got score " + + breeder.score + " and will propagate."); + String cmd[] = new String[] { "/bin/cp", "-f", dnaFile, seedFile }; + system(cmd); + } else { + System.out.println("Using existing " + seedFile); + } + } + return true; + } +} |
