001/* 002 * Java Genetic Algorithm Library (jenetics-3.7.0). 003 * Copyright (c) 2007-2016 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) 019 */ 020package org.jenetics.tool.evaluation; 021 022import static java.io.File.createTempFile; 023import static java.lang.String.format; 024import static java.nio.file.Files.deleteIfExists; 025import static java.util.Objects.requireNonNull; 026import static java.util.stream.Stream.concat; 027 028import java.io.IOException; 029import java.io.InputStream; 030import java.io.UncheckedIOException; 031import java.nio.file.Path; 032import java.nio.file.Paths; 033import java.util.Arrays; 034import java.util.HashMap; 035import java.util.List; 036import java.util.Map; 037import java.util.regex.Pattern; 038import java.util.stream.Collectors; 039import java.util.stream.DoubleStream; 040import java.util.stream.IntStream; 041import java.util.stream.Stream; 042 043import org.jenetics.internal.util.Args; 044 045import org.jenetics.tool.trial.Gnuplot; 046import org.jenetics.tool.trial.IO; 047import org.jenetics.tool.trial.Params; 048import org.jenetics.tool.trial.SampleSummary; 049import org.jenetics.tool.trial.TrialMeter; 050 051/** 052 * Helper class for creating Gnuplot diagrams from result files. 053 * 054 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> 055 * @version 3.7 056 * @since 3.4 057 */ 058public class Diagram { 059 060 /** 061 * The available Gnuplot templates. 062 */ 063 public static enum Template { 064 065 /** 066 * Template for execution time termination diagrams. 067 */ 068 EXECUTION_TIME("execution_time_termination"), 069 070 GENERATION_POPULATION_SIZE("generation_population_size"), 071 072 /** 073 * Template for fitness threshold termination diagrams. 074 */ 075 FITNESS_THRESHOLD("fitness_threshold_termination"), 076 077 /** 078 * Template for fitness threshold termination diagrams. 079 */ 080 FITNESS_CONVERGENCE("fitness_convergence_termination"), 081 082 /** 083 * Template for fixed generation termination diagrams. 084 */ 085 FIXED_GENERATION("fixed_generation_termination"), 086 087 /** 088 * Template for steady fitness termination diagrams, 089 */ 090 STEADY_FITNESS("steady_fitness_termination"), 091 092 /** 093 * Template for comparing different selectors. 094 */ 095 SELECTOR_COMPARISON("selector_comparison"), 096 097 POPULATION_SIZE("population_size"); 098 099 private final String _name; 100 private final String _path; 101 102 private Template(final String name) { 103 _name = requireNonNull(name); 104 _path = "/org/jenetics/tool/evaluation/" + 105 requireNonNull(name) + ".gp"; 106 } 107 108 public String getName() { 109 return _name; 110 } 111 112 /** 113 * Return the template content as string. 114 * 115 * @return the template content 116 */ 117 public String content() { 118 try (InputStream stream = Diagram.class.getResourceAsStream(_path)) { 119 return IO.toText(stream); 120 } catch (IOException e) { 121 throw new UncheckedIOException(e); 122 } 123 } 124 } 125 126 /** 127 * Create a performance diagram. 128 * 129 * @param input the input data 130 * @param template the Gnuplot template to use 131 * @param params the diagram parameters (x-axis) 132 * @param output the output file 133 * @param summary the first summary data 134 * @param summaries the rest of the summary data 135 * @throws IOException if the diagram generation fails 136 * @throws NullPointerException of one of the parameters is {@code null} 137 * @throws IllegalArgumentException if the {@code params}, {@code generation} 138 * and {@code fitness} doesn't have the same parameter count 139 */ 140 public static void create( 141 final Path input, 142 final Template template, 143 final Params<?> params, 144 final Path output, 145 final SampleSummary summary, 146 final SampleSummary... summaries 147 ) 148 throws IOException 149 { 150 final Stream<SampleSummary> summaryStream = Stream.concat( 151 Stream.of(summary), Stream.of(summaries) 152 ); 153 summaryStream.forEach(s -> { 154 if (params.size() != s.parameterCount()) { 155 throw new IllegalArgumentException(format( 156 "Parameters have different size: %d", params.size() 157 )); 158 } 159 }); 160 161 final Path templatePath = tempPath(); 162 try { 163 IO.write(template.content(), templatePath); 164 165 final Path dataPath = tempPath(); 166 try { 167 final String data = IntStream.range(0, params.size()) 168 .mapToObj(i -> toLineString(i, params, summary, summaries)) 169 .collect(Collectors.joining("\n")); 170 IO.write(data, dataPath); 171 172 final Gnuplot gnuplot = new Gnuplot(templatePath); 173 gnuplot.setEnv(params(input)); 174 gnuplot.create(dataPath, output); 175 } finally { 176 deleteIfExists(dataPath); 177 } 178 } finally { 179 deleteIfExists(templatePath); 180 } 181 } 182 183 private static Path tempPath() throws IOException { 184 return createTempFile("__diagram_template__", "__").toPath(); 185 } 186 187 private static String toLineString( 188 final int index, 189 final Params<?> params, 190 final SampleSummary summary, 191 final SampleSummary... summaries 192 ) { 193 return concat(concat( 194 Stream.of(params.get(index).toString().split(":")), 195 DoubleStream.of(summary.getPoints().get(index).toArray()) 196 .mapToObj(Double::toString)), 197 Stream.of(summaries) 198 .flatMapToDouble(s -> DoubleStream.of(s.getPoints().get(index).toArray())) 199 .mapToObj(Double::toString)) 200 .collect(Collectors.joining(" ")); 201 } 202 203 public static void main(final String[] arguments) throws Exception { 204 final Args args = Args.of(arguments); 205 206 final Path input = args.arg("input") 207 .map(Paths::get) 208 .map(Path::toAbsolutePath) 209 .get(); 210 211 final String[] samples = args.arg("samples") 212 .map(s -> s.split(",")) 213 .orElse(new String[]{"Generation", "Fitness"}); 214 215 final TrialMeter<Integer> trial = TrialMeter.read(input); 216 final Params<Integer> params = trial.getParams(); 217 final SampleSummary summary = trial.getData(samples[0]).summary(); 218 final SampleSummary[] summaries = Arrays.stream(samples, 1, samples.length) 219 .map(s -> trial.getData(s).summary()) 220 .toArray(SampleSummary[]::new); 221 222 create( 223 input, 224 template(input), 225 params, 226 output(input), 227 summary, 228 summaries 229 ); 230 } 231 232 private static Template template(final Path path) { 233 final String name = path.getFileName().toString() 234 .split("-")[1] 235 .split("\\.")[0]; 236 237 return Arrays.stream(Template.values()) 238 .filter(t -> t.getName().equals(name)) 239 .findFirst().get(); 240 } 241 242 private static Map<String, String> params(final Path path) { 243 System.out.println(path.getFileName()); 244 final List<String> parts = param(path.getFileName().toString()) 245 .flatMap(p -> Stream.of(p.split("@"))) 246 .collect(Collectors.toList()); 247 248 final Map<String, String> params = new HashMap<>(); 249 for (int i = 0; i < parts.size(); ++i) { 250 final String key = format("PARAM_%s", i); 251 params.put(key, parts.get(i)); 252 } 253 254 return params; 255 } 256 257 private static Stream<String> param(final String name) { 258 final String[] parts = name.split("-"); 259 return parts.length == 3 260 ? Stream.of(parts[2].split("\\.")[0]) 261 : Stream.empty(); 262 } 263 264 private static Path output(final Path path) { 265 final String name = path.getFileName().toString().split("\\.")[0]; 266 return Paths.get(path.getParent().toString(), name + ".svg"); 267 } 268 269}