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}