diff --git a/src/main/java/Enums/InitializationMethods.java b/src/main/java/Enums/InitializationMethods.java new file mode 100644 index 0000000000000000000000000000000000000000..3f3400f96fc50824c474913a2fff39c9cffdaf89 --- /dev/null +++ b/src/main/java/Enums/InitializationMethods.java @@ -0,0 +1,7 @@ +package Enums; + +public enum InitializationMethods { + Curl, + Straight, + Random +} diff --git a/src/main/java/Enums/MutatorMethods.java b/src/main/java/Enums/MutatorMethods.java new file mode 100644 index 0000000000000000000000000000000000000000..3ec0bb1c2283987189ca1034ee304336bfbd4bda --- /dev/null +++ b/src/main/java/Enums/MutatorMethods.java @@ -0,0 +1,6 @@ +package Enums; + +public enum MutatorMethods { + SinglePoint, + Crossover +} diff --git a/src/main/java/Enums/Selection.java b/src/main/java/Enums/SelectionMethods.java similarity index 67% rename from src/main/java/Enums/Selection.java rename to src/main/java/Enums/SelectionMethods.java index 251d89b3d42ff4681dfce8180188af476a522cdd..7a49b940e26553772a9a0327781da862149a8d68 100644 --- a/src/main/java/Enums/Selection.java +++ b/src/main/java/Enums/SelectionMethods.java @@ -1,6 +1,6 @@ package Enums; -public enum Selection { +public enum SelectionMethods { Proportional, Tournament, OnlyBest diff --git a/src/main/java/Enums/VisualizerMethods.java b/src/main/java/Enums/VisualizerMethods.java new file mode 100644 index 0000000000000000000000000000000000000000..30b14737a58affa1559912bf83de6ebcc29de16e --- /dev/null +++ b/src/main/java/Enums/VisualizerMethods.java @@ -0,0 +1,7 @@ +package Enums; + +public enum VisualizerMethods { + Console, + Image, + Video +} diff --git a/src/main/java/MainClasses/Config.java b/src/main/java/MainClasses/Config.java index 42dbc7e4b2ab9ab926d43991d3f5325d93afb0aa..42511f204d4fb6b3c81b0eec56c366ff602dee12 100644 --- a/src/main/java/MainClasses/Config.java +++ b/src/main/java/MainClasses/Config.java @@ -1,6 +1,9 @@ package MainClasses; -import Enums.Selection; +import Enums.InitializationMethods; +import Enums.MutatorMethods; +import Enums.SelectionMethods; +import Enums.VisualizerMethods; import java.awt.*; import java.io.BufferedInputStream; @@ -13,18 +16,33 @@ public class Config { String propertyPath; Properties properties; - static String LOGFILE; + static String ENCODING_VARIANT; + static int SEED; + + static int POPULATION_SIZE; static int TOTAL_GENERATIONS; + static InitializationMethods INITIALIZATION_METHOD; + static SelectionMethods SELECTION_METHOD; + static int K; // Number of selected Candidates to face off in a tournament selection + static MutatorMethods[] MUTATOR_METHODS; + static int POINTS_PER_BOND; // Points per hydrophobic bond, default Evaluator will work the same with any value + static int MUTATION_ATTEMPTS_PER_CANDIDATE; static double MUTATION_CHANCE; static double MUTATION_MULTIPLIER; static int CROSSOVER_ATTEMPTS_PER_CANDIDATE; static double CROSSOVER_CHANCE; static double CROSSOVER_MULTIPLIER; - static Selection SELECTION_VARIANT; - static int K; // Number of selected Candidates to face off in a tournament selection + + static String LOGFILE; + static VisualizerMethods[] VISUALIZERS; static String IMAGE_SEQUENCE_PATH; + static String VIDEO_PATH_AND_FILE; + static int IMAGE_INTERVAL; + static double IMAGE_INTERVAL_DECLINE; + static int IMAGE_INTERVAL_MIN; + static boolean ZOOM; // For images public static final Font font = new Font("Sans-Serif", Font.PLAIN, 15); @@ -47,9 +65,6 @@ public class Config { public static final String consoleConnectionVertical = " | "; public static final String consoleConnectionHorizontal = "---"; - // Points per hydrophobic bond - static int POINTS_PER_BOND; - public Config(String propertyPath) { this.propertyPath = propertyPath; this.properties = this.readProperties(); @@ -71,35 +86,76 @@ public class Config { } private void initializeProperties() { - LOGFILE = this.properties.getProperty("logfilePath"); + + // Basic Initialization settings + ENCODING_VARIANT = this.properties.getProperty("encodingVatiant"); + SEED = Integer.parseInt(this.properties.getProperty("seed")); + + + // Algorithm settings POPULATION_SIZE = Integer.parseInt(this.properties.getProperty("populationSize")); TOTAL_GENERATIONS = Integer.parseInt(this.properties.getProperty("noGenerations")); + if (this.properties.getProperty("initializationMethod").equals("curl")) { + INITIALIZATION_METHOD = InitializationMethods.Curl; + } else if (this.properties.getProperty("initializationMethod").equals("straight")) { + INITIALIZATION_METHOD = InitializationMethods.Straight; + }else if (this.properties.getProperty("initializationMethod").equals("random")) { + INITIALIZATION_METHOD = InitializationMethods.Random; + } + + if (this.properties.getProperty("selectionMethod").equals("proportional")) { + SELECTION_METHOD = SelectionMethods.Proportional; + } else if (this.properties.getProperty("selectionMethod").equals("tournament")) { + SELECTION_METHOD = SelectionMethods.Tournament; + } else if (this.properties.getProperty("selectionMethod").equals("onlybest")) { + SELECTION_METHOD = SelectionMethods.OnlyBest; + } + + K = Integer.parseInt(this.properties.getProperty("k")); + + String[] mutatorsToUse = this.properties.getProperty("mutatorMethods").split(","); + MUTATOR_METHODS = new MutatorMethods[mutatorsToUse.length]; + for (int i = 0; i < mutatorsToUse.length; i++) { + if (mutatorsToUse[i].equals("singlePoint")) { + MUTATOR_METHODS[i] = MutatorMethods.SinglePoint; + } else if (mutatorsToUse[i].equals("crossover")) { + MUTATOR_METHODS[i] = MutatorMethods.Crossover; + } + } + + POINTS_PER_BOND = Integer.parseInt(this.properties.getProperty("pointsPerBond")); + + // Mutation settings MUTATION_ATTEMPTS_PER_CANDIDATE = Integer.parseInt(this.properties.getProperty("mutationAttemptsPerCandidate")); MUTATION_CHANCE = Double.parseDouble(this.properties.getProperty("mutationChance")); - MUTATION_MULTIPLIER = Double.parseDouble(this.properties.getProperty("mutationDecline")); + MUTATION_MULTIPLIER = Double.parseDouble(this.properties.getProperty("mutationMultiplier")); CROSSOVER_ATTEMPTS_PER_CANDIDATE = Integer.parseInt(this.properties.getProperty("crossoverAttemptsPerCandidate")); CROSSOVER_CHANCE = Double.parseDouble(this.properties.getProperty("crossoverChance")); - CROSSOVER_MULTIPLIER = Double.parseDouble(this.properties.getProperty("crossoverDecline")); + CROSSOVER_MULTIPLIER = Double.parseDouble(this.properties.getProperty("crossoverMultiplier")); - K = Integer.parseInt(this.properties.getProperty("k")); - try { - if (this.properties.getProperty("selection").equals("proportional")) { - SELECTION_VARIANT = Selection.Proportional; - } else if (this.properties.getProperty("selection").equals("tournament")) { - SELECTION_VARIANT = Selection.Tournament; - } else if (this.properties.getProperty("selection").equals("onlybest")) { - SELECTION_VARIANT = Selection.Tournament; - } else { - throw new Exception("Selection variant not found!"); + // Output settings + LOGFILE = this.properties.getProperty("logfilePath"); + + String[] visualizersToUse = this.properties.getProperty("visualizerType").split(","); + VISUALIZERS = new VisualizerMethods[visualizersToUse.length]; + for (int i = 0; i < visualizersToUse.length; i++) { + if (visualizersToUse[i].equals("console")) { + VISUALIZERS[i] = VisualizerMethods.Console; + } else if (visualizersToUse[i].equals("image")) { + VISUALIZERS[i] = VisualizerMethods.Image; + } else if (visualizersToUse[i].equals("video")) { + VISUALIZERS[i] = VisualizerMethods.Video; } - } catch (Exception e) { - e.printStackTrace(); } - IMAGE_SEQUENCE_PATH = properties.getProperty("imageSequencePath"); - POINTS_PER_BOND = Integer.parseInt(this.properties.getProperty("pointsPerBond")); + IMAGE_SEQUENCE_PATH = this.properties.getProperty("imageSequencePath"); + VIDEO_PATH_AND_FILE = this.properties.getProperty("videoPathAndFile"); + IMAGE_INTERVAL = Integer.parseInt(this.properties.getProperty("imgInterval")); + IMAGE_INTERVAL_DECLINE = Double.parseDouble(this.properties.getProperty("imgIntervalDecline")); + IMAGE_INTERVAL_MIN = Integer.parseInt(this.properties.getProperty("imgIntervalMin")); + ZOOM = this.properties.getProperty("zoom").equals("true"); } public Properties getProperties() { diff --git a/src/main/java/MainClasses/GeneticAlgorithm.java b/src/main/java/MainClasses/GeneticAlgorithm.java index 6ba204f0307813411c91dcf603d560c5a7e4019c..ab395a5c920b5628551875ca2a3752cebd3a391a 100644 --- a/src/main/java/MainClasses/GeneticAlgorithm.java +++ b/src/main/java/MainClasses/GeneticAlgorithm.java @@ -1,12 +1,16 @@ package MainClasses; -import Enums.DirectionNESW; +import Enums.*; import Evaluators.EvaluatorNESW; import InitialGenerationCreators.Curl; +import InitialGenerationCreators.RandomDirection; +import InitialGenerationCreators.StraightLine; import Interfaces.*; import Mutators.Crossover; import Mutators.SinglePoint; +import Selectors.FitnessProportional; import Selectors.OnlyBest; +import Selectors.Tournament; import Visualization.Visualizers.VisualizerNESWtoConsole; import Visualization.Visualizers.VisualizerNESWtoFile; @@ -17,8 +21,7 @@ import java.nio.file.StandardOpenOption; import java.util.Random; public class GeneticAlgorithm { - Random rand = new Random(); - Visualizer visualizer; + Random rand; int[] isHydrophobic; Candidate[] population; @@ -32,32 +35,86 @@ public class GeneticAlgorithm { InitialGenerationCreator initialGenCreator; Mutator[] mutators; Selector selector; - Evaluator evaluator; + Visualizer[] visualizers; // Initialize with protein public GeneticAlgorithm (int[] protein) { this.isHydrophobic = protein; -// this.visualizer = new VisualizerNESWtoConsole(); - this.visualizer = new VisualizerNESWtoFile(Config.IMAGE_SEQUENCE_PATH); -// this.initialGenCreator = new RandomDirection<>(this.rand, DirectionNESW.class); -// this.initialGenCreator = new StraightLine(); - this.initialGenCreator = new Curl<>(DirectionNESW.class); + this.initializeSettings(); + this.clearLog(); + + this.population = this.initialGenCreator.initializeDirections(Config.POPULATION_SIZE, this.isHydrophobic); + this.totalFitness = 0; + this.fitness = new double[Config.POPULATION_SIZE]; + this.overallBestFitness = 0; + } + + private void initializeSettings() { + if (Config.SEED != -1) { + this.rand = new Random(Config.SEED); + } else { + this.rand = new Random(); + } + + // Settings that are dependant on encoding + if (Config.ENCODING_VARIANT.equals("NESW")) { + int nullCount = 0; + for (int i = 0; i < Config.VISUALIZERS.length; i++) { + if (!Config.VISUALIZERS[i].equals(VisualizerMethods.Console) + && !Config.VISUALIZERS[i].equals(VisualizerMethods.Image)) { + nullCount++; + } + } + this.visualizers = new Visualizer[Config.VISUALIZERS.length - nullCount]; + int j = 0; + for (VisualizerMethods vm : Config.VISUALIZERS) { + if (vm.equals(VisualizerMethods.Console)) { + this.visualizers[j] = new VisualizerNESWtoConsole(); + j++; + } else if (vm.equals(VisualizerMethods.Image)) { + this.visualizers[j] = new VisualizerNESWtoFile(Config.IMAGE_SEQUENCE_PATH); + j++; + } + } + + if (Config.INITIALIZATION_METHOD.equals(InitializationMethods.Curl)) { + this.initialGenCreator = new Curl<>(DirectionNESW.class); + } else if (Config.INITIALIZATION_METHOD.equals(InitializationMethods.Straight)) { + this.initialGenCreator = new StraightLine(); + } else if (Config.INITIALIZATION_METHOD.equals(InitializationMethods.Random)) { + this.initialGenCreator = new RandomDirection<>(DirectionNESW.class, this.rand); + } + + this.mutators = new Mutator[Config.MUTATOR_METHODS.length]; + for (int i = 0; i < Config.MUTATOR_METHODS.length; i++) { + if (Config.MUTATOR_METHODS[i].equals(MutatorMethods.SinglePoint)) { + this.mutators[i] = new SinglePoint<>(DirectionNESW.class, this.rand, + Config.MUTATION_ATTEMPTS_PER_CANDIDATE, Config.MUTATION_CHANCE, Config.MUTATION_MULTIPLIER); - this.mutators = new Mutator[2]; - this.mutators[0] = new SinglePoint<>(DirectionNESW.class, this.rand, Config.MUTATION_ATTEMPTS_PER_CANDIDATE, - Config.MUTATION_CHANCE, Config.MUTATION_MULTIPLIER); - this.mutators[1] = new Crossover<>(DirectionNESW.class, this.rand, Config.CROSSOVER_ATTEMPTS_PER_CANDIDATE, - Config.CROSSOVER_CHANCE, Config.CROSSOVER_MULTIPLIER); + } else if (Config.MUTATOR_METHODS[i].equals(MutatorMethods.Crossover)) { + this.mutators[i] = new Crossover<>(DirectionNESW.class, this.rand, + Config.CROSSOVER_ATTEMPTS_PER_CANDIDATE, Config.CROSSOVER_CHANCE, Config.CROSSOVER_MULTIPLIER); + } + } + + this.evaluator = new EvaluatorNESW(Config.POINTS_PER_BOND); -// this.selector = new FitnessProportional(this.rand, this.isHydrophobic); -// this.selector = new Tournament(this.rand, this.isHydrophobic, this.k); - this.selector = new OnlyBest(this.isHydrophobic); + } else { + // TODO: initialization for FRL settings + } - this.evaluator = new EvaluatorNESW(Config.POINTS_PER_BOND); + if (Config.SELECTION_METHOD.equals(SelectionMethods.Proportional)) { + this.selector = new FitnessProportional(this.rand, this.isHydrophobic); + } else if (Config.SELECTION_METHOD.equals(SelectionMethods.Tournament)) { + this.selector = new Tournament(this.rand, this.isHydrophobic, Config.K); + } else if (Config.SELECTION_METHOD.equals(SelectionMethods.OnlyBest)) { + this.selector = new OnlyBest(this.isHydrophobic); + } + } - // Clear log file + private void clearLog() { String content = "Generation\tAverage Fitness\tBest Fitness\tOverall Best Fitness\tBonds\tOverlaps\n"; try { Files.write(Paths.get(Config.LOGFILE), content.getBytes()); @@ -65,11 +122,6 @@ public class GeneticAlgorithm { } catch (IOException e) { e.printStackTrace(); } - - this.population = this.initialGenCreator.initializeDirections(Config.POPULATION_SIZE, this.isHydrophobic); - this.totalFitness = 0; - this.fitness = new double[Config.POPULATION_SIZE]; - this.overallBestFitness = 0; } public void simulateGenerations() { @@ -105,8 +157,10 @@ public class GeneticAlgorithm { int bonds = this.evaluator.evaluateBonds(this.population[bestIndex]); int overlaps = this.evaluator.evaluateOverlaps(this.population[bestIndex]); - this.visualizer.setFilename(String.format("gen_%07d.jpg",gen)); - this.visualizer.drawProtein(this.population[bestIndex].getVertexList(), bestFitness, bonds, overlaps, gen); + for (Visualizer v : this.visualizers) { + v.setFilename(String.format("gen_%07d.jpg", gen)); + v.drawProtein(this.population[bestIndex].getVertexList(), bestFitness, bonds, overlaps, gen); + } System.out.println("The fitness is: " + bestFitness + " [hydrophobicBonds = " + bonds + " | overlaps = " + overlaps + "]"); @@ -135,10 +189,22 @@ public class GeneticAlgorithm { } public int getMaxH() { - return this.visualizer.getMaxH(); + int maxHAcrossVisualiszators = 0; + for (Visualizer v : visualizers) { + if (maxHAcrossVisualiszators < v.getMaxH()) { + maxHAcrossVisualiszators = v.getMaxH(); + } + } + return maxHAcrossVisualiszators; } public int getMaxW() { - return this.visualizer.getMaxH(); + int maxWAcrossVisualiszators = 0; + for (Visualizer v : visualizers) { + if (maxWAcrossVisualiszators < v.getMaxW()) { + maxWAcrossVisualiszators = v.getMaxW(); + } + } + return maxWAcrossVisualiszators; } } diff --git a/src/main/java/MainClasses/Main.java b/src/main/java/MainClasses/Main.java index 8d21429f453ef2fd437bcb6fec336a0b7f029750..d378754103e4a7431f3784acd906424a539d96b7 100644 --- a/src/main/java/MainClasses/Main.java +++ b/src/main/java/MainClasses/Main.java @@ -1,25 +1,29 @@ package MainClasses; +import Enums.VisualizerMethods; +import Interfaces.Visualizer; import Visualization.VideoCreator; import java.io.IOException; +import java.util.Arrays; public class Main { public static void main(String[] args) { String propertyPath = "./src/main/resources/genetic.properties"; - Config config = new Config (propertyPath); + Config config = new Config(propertyPath); int[] protein = Examples.convertStringToIntArray(Examples.SEQ20); GeneticAlgorithm ga = new GeneticAlgorithm(protein); ga.simulateGenerations(); -// if (Config.isVidoable && Config.doVideo) - try { - VideoCreator.createVideo(config.getProperties(), ga.getMaxH(), ga.getMaxW()); - } catch (IOException e) { - e.printStackTrace(); + // Create a new video if possible and desired + boolean imagesRefreshed = Arrays.stream(Config.VISUALIZERS).anyMatch(VisualizerMethods.Image::equals); + boolean videoEnabled = Arrays.stream(Config.VISUALIZERS).anyMatch(VisualizerMethods.Video::equals); + if (imagesRefreshed && videoEnabled){ + VideoCreator.createVideo(Config.IMAGE_SEQUENCE_PATH, Config.VIDEO_PATH_AND_FILE, + Config.IMAGE_INTERVAL, ga.getMaxH(), ga.getMaxW()); } } } diff --git a/src/main/java/Visualization/VideoCreator.java b/src/main/java/Visualization/VideoCreator.java index af4ddb9981a921c993308195eebe7b68e63eab75..b5d9669a616e794d3d1f0353eaffc81fbe111032 100644 --- a/src/main/java/Visualization/VideoCreator.java +++ b/src/main/java/Visualization/VideoCreator.java @@ -87,27 +87,29 @@ public class VideoCreator{ } // MainClasses.Main function - public static void createVideo(Properties properties, int maxH, int maxW) throws IOException { - String imagesPath = properties.getProperty("imageSequencePath"); - String videoPathAndFile = properties.getProperty("videoPathAndFile"); - int imgInterval = Integer.parseInt(properties.getProperty("imgInterval")); - dir = new File(imagesPath); + public static void createVideo(String imagesPath, String videoPathAndFile, + int imgInterval, int maxH, int maxW) { + try { + dir = new File(imagesPath); - int[] widthHeight = VideoCreator.resizeImages(imagesPath, maxH, maxW); + int[] widthHeight = VideoCreator.resizeImages(imagesPath, maxH, maxW); - File file = new File(videoPathAndFile); - if (!file.exists()) { - file.createNewFile(); - } - Vector<String> imgLst = new Vector<>(); - if (dir.isDirectory()) { - int counter = 1; - for (final File f : dir.listFiles(imageFilter)) { - imgLst.add(f.getAbsolutePath()); + File file = new File(videoPathAndFile); + if (!file.exists()) { + file.createNewFile(); + } + Vector<String> imgLst = new Vector<>(); + if (dir.isDirectory()) { + int counter = 1; + for (final File f : dir.listFiles(imageFilter)) { + imgLst.add(f.getAbsolutePath()); + } } + makeVideo("file:\\" + file.getAbsolutePath(), imgLst, widthHeight[0], widthHeight[1], imgInterval); + } catch (IOException e) { + e.printStackTrace(); } - makeVideo("file:\\" + file.getAbsolutePath(), imgLst, widthHeight[0], widthHeight[1], imgInterval); } public static void makeVideo(String fileName, Vector imgLst, int width, int height, int interval) throws MalformedURLException { diff --git a/src/main/resources/genetic.properties b/src/main/resources/genetic.properties index 902bfa1549dac9490579cd63d2e802065205fc44..46e2035bdbc3ec1d07f728a26062b275532e6196 100644 --- a/src/main/resources/genetic.properties +++ b/src/main/resources/genetic.properties @@ -13,11 +13,11 @@ # Type of population initialization [curl | straight | random] initializationMethod = curl # Type of selection that should be used [proportional | tournament | onlybest] - selection = proportional + selectionMethod = proportional # Number of tournament participants, only relevant when selection is set to tournament k = 5 # Type(s) of mutators to use in the algorithm, separated by comma [singlePoint / crossover] - mutatorsToUse = singlePoint,crossover + mutatorMethods = singlePoint,crossover # Points each HH bond gives pointsPerBond = 1 @@ -28,13 +28,13 @@ # Chance for a successful single point mutation [1.0 = 100%] mutationChance = 1.0 # Multiplicand for mutation probability with each generation -> ex with 0.05: 1st 1.0, 2nd 0.95, 3rd 0.9025, 4th 0.857 - mutationDecline = 0.001 + mutationMultiplier = 0.001 # How often a crossover should be attempted per candidate crossoverAttemptsPerCandidate = 1 # Chance for a successful crossover [1.0 = 100%] crossoverChance = 0.6 # Multiplicand for mutation probability with each generation -> ex with -0.05: 1st 1.0, 2nd 1.05, 3rd 1,1025, 4th 1,157 - crossoverDecline = 0.005 + crossoverMultiplier = 0.005 ### Output settings @@ -48,9 +48,9 @@ videoPathAndFile = ./visualization/video.mp4 # ms between images in the beginning of the video imgInterval = 100 - # Decline of interval between images to speed up later generations + # Decline of interval between images to speed up later generations TODO imgIntervalDecline = 0.01 - # Minimum time between images + # Minimum time between images TODO imgIntervalMin = 10 - # Zoom to the appropriate size, once no bigger proteins are going to follow + # Zoom to the appropriate size, once no bigger proteins are going to follow TODO zoom = false