/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: GeneticPlacement.java
* Written by Team 3: Christian Wittner
*
* This code has been developed at the Karlsruhe Institute of Technology (KIT), Germany,
* as part of the course "Multicore Programming in Practice: Tools, Models, and Languages".
* Contact instructor: Dr. Victor Pankratius (pankratius@ipd.uka.de)
*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.placement.genetic1.g1;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import com.sun.electric.tool.placement.PlacementFrame;
import com.sun.electric.tool.placement.genetic1.Chromosome;
import com.sun.electric.tool.placement.genetic1.Population;
import com.sun.electric.tool.placement.genetic1.PopulationCreation;
import com.sun.electric.util.math.Orientation;
/**
* Genetic placement test framework class. Allows to plug in to utilize
* different mutation, crossover, initial population creation and selection
* algorithms.
*/
public class GeneticPlacement extends PlacementFrame {
public PlacementParameter maxThreadsParam = new PlacementParameter("threads", "Number of threads:", 4);
public PlacementParameter maxRuntimeParam = new PlacementParameter("runtime", "Runtime (seconds):", 240);
public int numThreads;
public int maxRuntime;
boolean printDebugInformation;
private int generation;
private PopulationCreation popCreator;
private Population population;
static ThreadPoolExecutor threadpool;
private double previousFittestSolutionValue = Double.MAX_VALUE;
double improvementRate;
private Chromosome fittestSolution;
boolean isMemoryBarrierReached;
Runtime rt;
ArrayList<Population> subPopulations;
private long startTotal;
private long stopTotal;
private int placementWidth = -1;
public static Random randomGenerator;
public final static double PlacementWidthRatio = 1.2;
public void setParamterValues(int threads, int runtime, boolean debug) {
maxRuntime = runtime;
numThreads = threads;
printDebugInformation = debug;
this.numOfThreads = threads;
this.runtime = runtime;
}
public static Random getRandomGenerator() {
return randomGenerator;
}
public static PlacementNodeProxy[] nodeProxies;
// default values used for placement calculations
// some can be altered through constructor call
static int NBR_OF_THREADS;
final static int EPOCH_LENGTH_START = 40;
final static int EPOCH_LENGTH_MIN = 20;
final static int EPOCH_LENGTH_MAX = 400;
final static int EPOCH_LENGTH_STEP = 2;
final static double EPOCH_LENGTH_RAISE = 0.005;
final static double EPOCH_LENGTH_LOWER = 0.05;
public static int current_epoch_length;
final static int POPULATION_SIZE_PER_THREAD_START = 64;
final static int POPULATION_SIZE_PER_THREAD_MIN = 32;
final static int POPULATION_SIZE_PER_THREAD_MAX = 1024;
final static int POPULATION_SIZE_PER_THREAD_STEP = 16;
final static double POPULATION_SIZE_PER_THREAD_RAISE = 0.005;
final static double POPULATION_SIZE_PER_THREAD_LOWER = 0.05;
static int current_population_size_per_thread;
public static long MAX_RUNTIME;
public static long START_TIME;
// log level and their meaning
// OFF
// SEVERE (highest value)
// WARNING
// INFO Whats happening in the master thread.
// CONFIG
// FINE Control flow of worker threads
// FINER Genetic operations
// FINEST (lowes) Datastructure level: Gene Chromsome etc.
//
// ALL
//
// level that will be displayed for all!
final static Level LOGGER_LEVEL = Level.ALL;
// level of this class each class should have this!
final static Level LOG_LEVEL = Level.INFO;
public final static boolean IS_PROGRESS_LOGGING_ENABLED = false;
static String PROGRESS_LOG_FILENAME;
static File PROGRESS_LOG_FILE;
public static PrintWriter PROGRESS_LOGGER;
// over all switch to disable code passages for logging
public final static boolean IS_LOGGING_ENABLED = false;
final static boolean DEBUG = true;
final static boolean MEASURE_PERFORMANCE = false;
final static String LOG_FILE_NAME = "genetic.log";
static Calendar calendar;
public static Logger logger;
void init() {
// reset variable values to default start values
current_population_size_per_thread = POPULATION_SIZE_PER_THREAD_START;
current_epoch_length = EPOCH_LENGTH_START;
// use configuration evantually set by setBenchmark
NBR_OF_THREADS = numThreads;
MAX_RUNTIME = System.currentTimeMillis() + maxRuntime * 1000;
START_TIME = System.currentTimeMillis();
threadpool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NBR_OF_THREADS);
subPopulations = new ArrayList<Population>(NBR_OF_THREADS);
}
public static ExecutorService getThreadPool() {
return threadpool;
}
static List<PlacementNetwork> allNetworks;
public GeneticPlacement() {
maxRuntime = 30;
numThreads = Runtime.getRuntime().availableProcessors();
popCreator = new PopulationCreationRandomWithPlaceHolder2(new Random(System.currentTimeMillis()));
// popCreator = new PopulationOptimizedCreation();
rt = Runtime.getRuntime();
if (IS_PROGRESS_LOGGING_ENABLED) {
PROGRESS_LOG_FILENAME = "progress_log_";
Calendar now = Calendar.getInstance();
PROGRESS_LOG_FILENAME = PROGRESS_LOG_FILENAME.concat(now.get(Calendar.YEAR)
+ now.get(Calendar.MONTH) + now.get(Calendar.DAY_OF_MONTH) + "_"
+ now.get(Calendar.HOUR_OF_DAY) + now.get(Calendar.MINUTE) + now.get(Calendar.SECOND)
+ ".log");
PROGRESS_LOG_FILE = new File(PROGRESS_LOG_FILENAME);
try {
PROGRESS_LOGGER = new PrintWriter(PROGRESS_LOG_FILE);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (IS_LOGGING_ENABLED) {
logger = Logger.getLogger(GeneticPlacement.class.getName());
logger.setLevel(LOGGER_LEVEL);
try {
Handler fh = new FileHandler(LOG_FILE_NAME, true);
fh.setFormatter(new SimpleFormatter());
logger.addHandler(fh);
} catch (SecurityException e1) {
System.err.println("couldn't create/append to log file " + LOG_FILE_NAME);
System.exit(-1);
} catch (IOException e1) {
System.err.println("couldn't create/append to log file " + LOG_FILE_NAME);
System.exit(-1);
}
}
randomGenerator = new Random(System.currentTimeMillis());
}
@Override
public String getAlgorithmName() {
return "Genetic-1";
}
private static boolean beenRun = false;
@Override
public void runPlacement(List<PlacementNode> nodesToPlace, List<PlacementNetwork> allNetworks,
String cellName) {
this.setParamterValues(this.maxThreadsParam.getIntValue(), this.maxRuntimeParam.getIntValue(), false);
if (beenRun)
System.out
.println("WARNING: The Genetic-1 placement code is not reentrant and can be run only once in an Electric session.");
beenRun = true;
init();
if (GeneticPlacement.IS_LOGGING_ENABLED) System.out.println("nodes :" + nodesToPlace.size());
if (GeneticPlacement.IS_LOGGING_ENABLED)
logger.log(LOG_LEVEL, "cell:" + cellName + " nodes:" + nodesToPlace.size() + " threads:"
+ NBR_OF_THREADS + " population per thread:" + POPULATION_SIZE_PER_THREAD_START
+ "epoch:" + current_epoch_length);
if (GeneticPlacement.IS_LOGGING_ENABLED)
logger.log(LOG_LEVEL, "start wrapping placement nodes in proxies");
// wrap placement nodes in proxy classes
GeneticPlacement.nodeProxies = new PlacementNodeProxy[nodesToPlace.size()];
for (int i = 0; i < nodesToPlace.size(); i++) {
nodeProxies[i] = new PlacementNodeProxy(nodesToPlace.get(i));
}
if (GeneticPlacement.IS_LOGGING_ENABLED)
logger.log(LOG_LEVEL, "done wrapping placement nodes in proxies");
if (MEASURE_PERFORMANCE) {
startTotal = System.currentTimeMillis();
}
GeneticPlacement.allNetworks = allNetworks;
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "start population creation");
spawnInitalPopulation(nodeProxies);
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "done population creation");
assert (population.chromosomes.size() % NBR_OF_THREADS == 0);
subPopulations.clear();
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "start partitioning population");
// Initially partition population and distribute to the threads/cpus
{
while (!population.chromosomes.isEmpty()) {
// create subpopulation
Population subPopulation = new Population(POPULATION_SIZE_PER_THREAD_START);
// assign subset of chromosomes to subpopulation
for (int i = 0; i < POPULATION_SIZE_PER_THREAD_START; i++) {
subPopulation.chromosomes.add(population.chromosomes.remove(0));
}
subPopulations.add(subPopulation);
}
}
assert (subPopulations.size() == NBR_OF_THREADS);
assert (population.chromosomes.size() == 0);
ArrayList<SubPopulationProcessing> tasks = new ArrayList<SubPopulationProcessing>(NBR_OF_THREADS);
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "done partitioning population");
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "start parallel processing loop");
// create worker threads
for (int i = 0; i < NBR_OF_THREADS; i++) {
// TODO: nbrOfGeneration has to be a multiple of EPOCH_LENGTH
// TODO: does task have to be cleaned manually?
tasks.add(new SubPopulationProcessing(current_epoch_length, randomGenerator.nextLong(),
placementWidth, nodeProxies, allNetworks,
subPopulations.get(0).chromosomes.get(0).Index2GenePositionInChromosome.length));
}
PopulationMutation2.resetMutationRates();
// for (generation = 0; generation < NBR_OF_GENERATIONS; generation +=
// EPOCH_LENGTH
// * NBR_OF_THREADS) {
if (GeneticPlacement.IS_LOGGING_ENABLED)
System.out.println("current: " + System.currentTimeMillis() + " start: " + START_TIME
+ " diff sec: " + (System.currentTimeMillis() - START_TIME) / 1000);
while (System.currentTimeMillis() < MAX_RUNTIME) {
// have cpus calculate a epoch length
for (int i = 0; i < NBR_OF_THREADS; i++) {
tasks.get(i).setSubPolulation(subPopulations.get(i));
}
// check that each chromosome is only in one of the subpopulations
assert (isChromosomeOnlyInOneSubPopulation());
assert (tasks.size() == NBR_OF_THREADS);
// execute threads in pool and wait for their termination
try {
List<Callable<Population>> copyList = new ArrayList<Callable<Population>>();
for (SubPopulationProcessing pop : tasks)
copyList.add(pop);
List<Future<Population>> threadResults = threadpool.invokeAll(copyList);
assert (threadpool.getPoolSize() == NBR_OF_THREADS);
// verify if all tasks terminated normally
for (Future<Population> f : threadResults) {
f.get();
assert (f.isDone());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// sort solutions by fitness and evenly distribute to the threads
collectAndRedistributeIndividuals();
// return best result of the global population
if (GeneticPlacement.IS_LOGGING_ENABLED)
System.out.println("Best fitness value: " + fittestSolution.fitness + " improvement rate: "
+ improvementRate);
}
if (GeneticPlacement.IS_LOGGING_ENABLED) logger.log(LOG_LEVEL, "done parallel processing loop");
// TODO: apply placement of fittest solution
// TODO: call our placement routine on the gene representation of the
// placement nodes
// should eventually done by evaluate method in the chromosome as it is
// a
// prerequisite
// to calculate the metric
// if fittest solution == null we probably haven't had time to finish at
// least one
// epoch!
if (fittestSolution != null) {
// apply gene placement to referenced placement nodes
for (int i = 0; i < nodesToPlace.size(); i++) {
nodesToPlace.get(i).setPlacement(fittestSolution.GeneXPos[i], fittestSolution.GeneYPos[i]);
nodesToPlace.get(i).setOrientation(Orientation.fromAngle(fittestSolution.GeneRotation[i]));
}
}
// write to images
// PNGOut.write(fittestSolution);
// write fittestSolution to xml
// XMLStorage.writeChromosome2File(fittestSolution, "fittestChromsome"
// + System.currentTimeMillis() + ".xml");
// Chromosome cXML = XMLStorage.loadChromosome("fittestChromsome.xml");
// assert (XMLStorage.isHavingSameValues(fittestSolution, cXML));
if (MEASURE_PERFORMANCE) {
stopTotal = System.currentTimeMillis();
System.out.println("total placement time: " + (stopTotal - startTotal) / 1000f + "sec");
}
threadpool.shutdownNow();
}
private void collectAndRedistributeIndividuals() {
// sort all subpopulations
for (Population p : subPopulations) {
Collections.sort(p.chromosomes);
}
// pick any as when this code is reached first fittestSolution might
// still be null
fittestSolution = subPopulations.get(0).chromosomes.get(0);
// remember fittest solution so far
for (Population subp : subPopulations) {
if (subp.chromosomes.get(0).fitness.doubleValue() < fittestSolution.fitness.doubleValue())
fittestSolution = subp.chromosomes.get(0);
}
// replace the weakest solutions of each subpopulation with the
// top performers of the other subpopulations
{
// calculate how many we can transfer
// has to work with different nbr of threads an population sizes
int subPopSize = subPopulations.get(0).chromosomes.size();
// optimum pick 7% of top of each list
int nbrOfExchangeIndividuals = (int) (.7 * subPopSize);
// trim down as no more than 25% of a subpopulation should be
// altered
if (nbrOfExchangeIndividuals * numThreads > .25 * subPopSize)
nbrOfExchangeIndividuals = (int) ((.25 * subPopSize) / numThreads);
if (nbrOfExchangeIndividuals < 1) nbrOfExchangeIndividuals = 1;
// now exchange the calculated numbers
for (Population subp1 : subPopulations) {
for (Population subp2 : subPopulations) {
if (subp1 != subp2) {
for (int i = 0; i < nbrOfExchangeIndividuals; i++) {
// get chromosome of subp1 and copy information
// copy is requirred not to have two thread
// concurrently access one chromosome
Chromosome c1 = subp1.chromosomes.get(subp1.chromosomes.size() - 1 - i);
Chromosome c2 = subp2.chromosomes.get(i);
c1.altered = c2.altered;
c1.fitness = c2.fitness;
for (int rotation = 0; rotation < c1.GeneRotation.length; rotation++) {
c1.GeneRotation[rotation] = c2.GeneRotation[rotation];
}
for (int xpadding = 0; xpadding < c1.GeneXPadding.length; xpadding++) {
c1.GeneXPadding[xpadding] = c2.GeneXPadding[xpadding];
}
for (int ypadding = 0; ypadding < c1.GeneYPadding.length; ypadding++) {
c1.GeneYPadding[ypadding] = c2.GeneYPadding[ypadding];
}
for (int xpos = 0; xpos < c1.GeneXPos.length; xpos++) {
c1.GeneXPos[xpos] = c2.GeneXPos[xpos];
}
for (int ypos = 0; ypos < c1.GeneYPos.length; ypos++) {
c1.GeneYPos[ypos] = c2.GeneYPos[ypos];
}
for (int index = 0; index < c1.Index2GenePositionInChromosome.length; index++) {
c1.Index2GenePositionInChromosome[index] = c2.Index2GenePositionInChromosome[index];
}
}
}
}
}
}
generation += current_epoch_length * NBR_OF_THREADS;
{
// calculate improvement rate and set mutation rate accordingly
improvementRate = (previousFittestSolutionValue - fittestSolution.fitness.doubleValue())
/ previousFittestSolutionValue;
previousFittestSolutionValue = fittestSolution.fitness.doubleValue();
isMemoryBarrierReached = rt.totalMemory() == rt.maxMemory()
&& rt.freeMemory() < 0.1 * rt.maxMemory();
// if improvementRate = 1 don't touch a thing
// probably just the first evaluation :)
if (improvementRate < 1) {
if (!isMemoryBarrierReached && improvementRate > PopulationMutation2.MUTATION_RATE_INCREASE)
PopulationMutation2.increaseMutationRate();
else
if (improvementRate < PopulationMutation2.MUTATION_RATE_LOWER)
PopulationMutation2.lowerMutationRate();
// adapt population size based on progress
if (!isMemoryBarrierReached && improvementRate < POPULATION_SIZE_PER_THREAD_RAISE
&& current_population_size_per_thread < POPULATION_SIZE_PER_THREAD_MAX) {
current_population_size_per_thread += POPULATION_SIZE_PER_THREAD_STEP;
} else
if (improvementRate > POPULATION_SIZE_PER_THREAD_LOWER
&& current_population_size_per_thread > POPULATION_SIZE_PER_THREAD_MIN)
current_population_size_per_thread -= POPULATION_SIZE_PER_THREAD_STEP;
// adapt epoch length based on progress
if (!isMemoryBarrierReached && improvementRate < EPOCH_LENGTH_RAISE
&& current_epoch_length < EPOCH_LENGTH_MAX) {
current_epoch_length += EPOCH_LENGTH_STEP;
} else
if (improvementRate > EPOCH_LENGTH_LOWER && current_epoch_length > EPOCH_LENGTH_MIN)
current_epoch_length -= EPOCH_LENGTH_STEP;
}
// in debug print information
if (GeneticPlacement.IS_LOGGING_ENABLED)
logger.log(LOG_LEVEL, "Generation " + generation + " best fitness value: "
+ fittestSolution.fitness + " improvement rate: " + improvementRate);
}
}
private void spawnInitalPopulation(PlacementNodeProxy[] nodeProxies) {
// calculate all cell area
int allCellArea = 0;
for (PlacementNodeProxy proxy : nodeProxies) {
allCellArea += proxy.width * proxy.height;
}
// calculate placementWidth
allCellArea *= PlacementWidthRatio;
placementWidth = (int) Math.sqrt(allCellArea);
// TODO:make parallel
{
// create initial population
population = popCreator.generatePopulation(nodeProxies, allNetworks,
POPULATION_SIZE_PER_THREAD_START * NBR_OF_THREADS);
assert (population.chromosomes.size() == POPULATION_SIZE_PER_THREAD_START * NBR_OF_THREADS);
}
}
/**
* Use for assertion that a chromosome is only in one subpopulation.
*
* @return true if each chromosome only in one subpopulation false
* otherwise.
*/
private boolean isChromosomeOnlyInOneSubPopulation() {
Population sp1, sp2;
for (int i = 0; i < subPopulations.size(); i++) {
sp1 = subPopulations.get(i);
for (Chromosome c : sp1.chromosomes)
for (int ii = i + 1; ii < subPopulations.size(); ii++) {
sp2 = subPopulations.get(ii);
if (sp1 != sp2 && sp2.chromosomes.contains(c)) return false;
}
}
return true;
}
/**
* @deprecated
*
* Don't use. For Performance test Purpose only.
*
* @param nets
*/
public static void setAllNetworks(List<PlacementNetwork> nets) {
allNetworks = nets;
}
public static List<PlacementNetwork> getAllNetworks() {
return allNetworks;
}
public static int getNBR_OF_THREADS() {
return NBR_OF_THREADS;
}
public static int getPOPULATION_SIZE() {
return POPULATION_SIZE_PER_THREAD_START;
}
public void setNbrOfThreads(int nbrOfThreads) {
NBR_OF_THREADS = nbrOfThreads;
// TODO: eventual overhead as it done already in init()
threadpool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NBR_OF_THREADS);
subPopulations = new ArrayList<Population>(NBR_OF_THREADS);
}
public void setEpochLength(int epochLength) {
current_epoch_length = epochLength;
}
public void setPopulationSizePerThread(int populationSize) {
current_population_size_per_thread = populationSize;
}
// full telemetry output to log file
void logProgress(int population_size) {
GeneticPlacement.PROGRESS_LOGGER
.println((System.currentTimeMillis() - GeneticPlacement.START_TIME) / 1000 + ";" + "main"
+ ";" + generation + ";" + fittestSolution.fitness + ";" + population_size + ";"
+ GeneticPlacement.current_population_size_per_thread + ";"
+ PopulationMutation2.chromosomeAlterPaddingRate + ";"
+ PopulationMutation2.genePaddingChangeRate_current + ";"
+ PopulationMutation2.chrosomeMaxPaddingChangeStep + ";"
+ PopulationMutation2.chromosomeMoveRate + ";"
+ PopulationMutation2.geneMoveRate_current + ";"
+ PopulationMutation2.geneMoveDistance + ";" + PopulationMutation2.chromosomeSwapRate
+ ";" + PopulationMutation2.geneSwapRate_current + ";"
+ PopulationMutation2.chromsomeRotationRate + ";" + improvementRate);
}
public int getRUNTIME() {
return maxRuntime;
}
}