001    /*
002     * Copyright 2011 Christian Kumpe http://kumpe.de/christian/java
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package de.kumpe.hadooptimizer.examples;
017    
018    import java.util.Date;
019    
020    import org.apache.commons.cli.Option;
021    import org.apache.commons.cli.OptionGroup;
022    import org.apache.commons.cli.Options;
023    import org.apache.commons.cli.ParseException;
024    import org.apache.commons.cli.PatternOptionBuilder;
025    import org.apache.hadoop.conf.Configurable;
026    
027    import de.kumpe.hadooptimizer.EaOptimizerConfiguration;
028    import de.kumpe.hadooptimizer.EaOptimizerConfigurationBase;
029    import de.kumpe.hadooptimizer.EsIndividual;
030    import de.kumpe.hadooptimizer.EsOptimizerConfiguration;
031    import de.kumpe.hadooptimizer.EsWrappableMutator;
032    import de.kumpe.hadooptimizer.Halter;
033    import de.kumpe.hadooptimizer.Mutator;
034    import de.kumpe.hadooptimizer.Optimizer;
035    import de.kumpe.hadooptimizer.Recombiner;
036    import de.kumpe.hadooptimizer.hadoop.EaHadoOptimizer;
037    import de.kumpe.hadooptimizer.hadoop.EsHadoOptimizer;
038    import de.kumpe.hadooptimizer.hadoop.EsMultiPopulationsHadoOptimizer;
039    import de.kumpe.hadooptimizer.impl.ComputingTimeLimitHalter;
040    import de.kumpe.hadooptimizer.impl.CountingHalter;
041    import de.kumpe.hadooptimizer.impl.DuplicatingRecombiner;
042    import de.kumpe.hadooptimizer.impl.EsEvaluatorWrapper;
043    import de.kumpe.hadooptimizer.impl.EsHalterWrapper;
044    import de.kumpe.hadooptimizer.impl.EsMutatorWrapper;
045    import de.kumpe.hadooptimizer.impl.EsPopulationReaderWrapper;
046    import de.kumpe.hadooptimizer.impl.EsPopulationWriterWrapper;
047    import de.kumpe.hadooptimizer.impl.EvaluationLimitHalter;
048    import de.kumpe.hadooptimizer.impl.LoggingReporter;
049    import de.kumpe.hadooptimizer.impl.MemoryPopulationReader;
050    import de.kumpe.hadooptimizer.impl.NullPopulationWriter;
051    import de.kumpe.hadooptimizer.impl.ReportingHalterWrapper;
052    import de.kumpe.hadooptimizer.impl.ReportingHalterWrapper.Reporter;
053    import de.kumpe.hadooptimizer.impl.TextFileReporter;
054    import de.kumpe.hadooptimizer.impl.TimeBasedHalter;
055    import de.kumpe.hadooptimizer.simple.SimpleEaOptimizer;
056    import de.kumpe.hadooptimizer.simple.SimpleEsOptimizer;
057    import de.kumpe.hadooptimizer.simple.ThreadedEaOptimizer;
058    import de.kumpe.hadooptimizer.simple.ThreadedEsOptimizer;
059    
060    /**
061     * Base class for all "executables" in the examples module. It delivers a basic
062     * infrastructure for a common workflow:
063     * <ol>
064     * <li>{@link #createOptions()}
065     * <li>{@link #processCommandLine()}
066     * <li>{@link #execute()}
067     * </ol>
068     * 
069     * @param <I>
070     *            the individual's type
071     * 
072     * @author <a href="http://kumpe.de/christian/java">Christian Kumpe</a>
073     */
074    public abstract class OptimizerExample<I> extends Example {
075            protected static final String OPTION_STANDARD_DEVIATION = "d";
076            protected static final String OPTION_ZERO_PADDING = "z";
077            protected static final String OPTION_MULTI_POPULATIONS = "m";
078            protected static final String OPTION_ES = "es";
079            protected static final String OPTION_EA = "ea";
080            protected static final String OPTION_OFFSPRING = "o";
081            protected static final String OPTION_PRESERVE_PARENTS = "k";
082            protected static final String OPTION_EVALUATION_LIMIT = "limit";
083            protected static final String OPTION_TIME_LIMIT = "time";
084            protected static final String OPTION_NS_LIMIT = "ns";
085            protected static final String OPTION_CYCLES = "n";
086            protected static final String OPTION_PARENTS = "p";
087    
088            protected int parents;
089            protected boolean preserveParents;
090            protected int offspring;
091            protected double standardDeviation = 1d;
092            private ReportingHalterWrapper<I> reportingHalterWrapper;
093    
094            @Override
095            protected Options createOptions() throws Exception {
096                    final Options options = super.createOptions();
097    
098                    final OptionGroup haltingGroup = new OptionGroup();
099                    haltingGroup.setRequired(true);
100    
101                    final Option cyclesOption = new Option(OPTION_CYCLES, true,
102                                    "limit number of cycles");
103                    cyclesOption.setArgName("nrOfCycles");
104                    cyclesOption.setType(PatternOptionBuilder.NUMBER_VALUE);
105                    haltingGroup.addOption(cyclesOption);
106    
107                    final Option nsLimitOption = new Option(OPTION_NS_LIMIT, true,
108                                    "limit computing time by number of ns");
109                    nsLimitOption.setArgName("nrOfNs");
110                    nsLimitOption.setType(PatternOptionBuilder.NUMBER_VALUE);
111                    haltingGroup.addOption(nsLimitOption);
112    
113                    final Option timeLimitOption = new Option(OPTION_TIME_LIMIT, true,
114                                    "limit computing time until specified date and time");
115                    timeLimitOption.setArgName("dateTime");
116                    timeLimitOption.setType(PatternOptionBuilder.DATE_VALUE);
117                    haltingGroup.addOption(timeLimitOption);
118    
119                    final Option evaluationLimitOption = new Option(
120                                    OPTION_EVALUATION_LIMIT, true,
121                                    "optimize until the specified evaluation is reached");
122                    evaluationLimitOption.setArgName("dateTime");
123                    evaluationLimitOption.setType(PatternOptionBuilder.NUMBER_VALUE);
124                    haltingGroup.addOption(evaluationLimitOption);
125    
126                    options.addOptionGroup(haltingGroup);
127    
128                    final Option parentsOption = new Option(OPTION_PARENTS, "parents",
129                                    true, "number of parents");
130                    parentsOption.setArgName("nrOfParents");
131                    parentsOption.setType(PatternOptionBuilder.NUMBER_VALUE);
132                    options.addOption(parentsOption);
133    
134                    final Option preserveParentsOption = new Option(
135                                    OPTION_PRESERVE_PARENTS, "keepParents", true,
136                                    "whether to keep parents");
137                    preserveParentsOption.setOptionalArg(true);
138                    preserveParentsOption.setArgName("true|false");
139                    options.addOption(preserveParentsOption);
140    
141                    final Option offspringOption = new Option(OPTION_OFFSPRING,
142                                    "offspring", true, "number of offspring");
143                    offspringOption.setArgName("nrOfOffspring");
144                    offspringOption.setType(PatternOptionBuilder.NUMBER_VALUE);
145                    options.addOption(offspringOption);
146    
147                    final OptionGroup typeOptionGroup = new OptionGroup();
148    
149                    final Option eaOption = new Option(OPTION_EA, true,
150                                    "use an EA-optimizer of the sepcified type");
151                    eaOption.setOptionalArg(true);
152                    eaOption.setArgName("simple|threaded|hadoop");
153                    typeOptionGroup.addOption(eaOption);
154    
155                    final Option esOption = new Option(OPTION_ES, true,
156                                    "use an ES-optimizer of the sepcified type");
157                    esOption.setOptionalArg(true);
158                    esOption.setArgName("simple|threaded|hadoop");
159                    typeOptionGroup.addOption(esOption);
160    
161                    options.addOptionGroup(typeOptionGroup);
162    
163                    final Option multiPopulationsOption = new Option(
164                                    OPTION_MULTI_POPULATIONS,
165                                    "multiPopulation",
166                                    true,
167                                    "use the multi-population optimizer with the given number of inner cycles; only valid with -es hadoop");
168                    multiPopulationsOption.setOptionalArg(true);
169                    multiPopulationsOption.setArgName("nrOfInnerCycles");
170                    multiPopulationsOption.setType(PatternOptionBuilder.NUMBER_VALUE);
171                    options.addOption(multiPopulationsOption);
172    
173                    final Option zeroPaddingOption = new Option(
174                                    OPTION_ZERO_PADDING,
175                                    "zeroPadding",
176                                    false,
177                                    "when an individual is given on the command line, fill missing elements with zeros");
178                    options.addOption(zeroPaddingOption);
179    
180                    final Option standardDeviationOption = new Option(
181                                    OPTION_STANDARD_DEVIATION, "standardDeviation", true,
182                                    "the standard deviation of a gaussion mutator");
183                    standardDeviationOption.setArgName("standardDeviation");
184                    standardDeviationOption.setType(PatternOptionBuilder.NUMBER_VALUE);
185                    options.addOption(standardDeviationOption);
186    
187                    return options;
188            }
189    
190            @Override
191            protected void processCommandLine() throws Exception {
192                    super.processCommandLine();
193    
194                    final Number parents = (Long) commandLine
195                                    .getParsedOptionValue(OPTION_PARENTS);
196                    if (null != parents) {
197                            this.parents = parents.intValue();
198    
199                    }
200    
201                    if (commandLine.hasOption(OPTION_PRESERVE_PARENTS)) {
202                            preserveParents = Boolean.parseBoolean(commandLine.getOptionValue(
203                                            OPTION_PRESERVE_PARENTS, "true"));
204                    }
205    
206                    final Number offspring = (Long) commandLine
207                                    .getParsedOptionValue(OPTION_OFFSPRING);
208                    if (null != offspring) {
209                            this.offspring = offspring.intValue();
210                    }
211    
212                    final Number standardDeviation = (Number) commandLine
213                                    .getParsedOptionValue(OPTION_STANDARD_DEVIATION);
214                    if (null != standardDeviation) {
215                            this.standardDeviation = standardDeviation.doubleValue();
216                    }
217            }
218    
219            @Override
220            protected void execute() throws Exception {
221                    final Optimizer<?> optimizer;
222                    if (commandLine.hasOption(OPTION_EA)) {
223                            optimizer = createEaOptimizer();
224                    } else {
225                            optimizer = createEsOptimizer();
226                    }
227    
228                    if (optimizer instanceof Configurable) {
229                            ((Configurable) optimizer).setConf(getConf());
230                    }
231    
232                    optimizer.optimize();
233            }
234    
235            protected EaOptimizerConfiguration<I> createEaOptimizerConfiguration()
236                            throws ParseException {
237                    throw new UnsupportedOperationException(
238                                    "this example does not support EA-configurations");
239            }
240    
241            protected EsOptimizerConfiguration createEsOptimizerConfiguration()
242                            throws ParseException {
243                    @SuppressWarnings("unchecked")
244                    final EaOptimizerConfiguration<double[]> eaConf = (EaOptimizerConfiguration<double[]>) createEaOptimizerConfiguration();
245                    eaConf.validate();
246                    final EsOptimizerConfiguration esConf = new EsOptimizerConfiguration();
247                    esConf.setPopulationReader(new EsPopulationReaderWrapper(eaConf
248                                    .getPopulationReader()));
249                    esConf.setPopulationWriter(new EsPopulationWriterWrapper(eaConf
250                                    .getPopulationWriter()));
251                    esConf.setHalter(new EsHalterWrapper(eaConf.getHalter()));
252                    final Mutator<double[]> delegatesMutator = eaConf.getMutator();
253                    if (!(delegatesMutator instanceof EsWrappableMutator)) {
254                            throw new UnsupportedOperationException(
255                                            "cannot wrap the delegate's Mutator: " + delegatesMutator);
256                    }
257                    esConf.setMutator(new EsMutatorWrapper(
258                                    (EsWrappableMutator) delegatesMutator));
259                    esConf.setEvaluator(new EsEvaluatorWrapper(eaConf.getEvaluator()));
260                    esConf.setParents(eaConf.getParents());
261                    esConf.setPreserveParents(eaConf.isPreserveParents());
262    
263                    final Recombiner<double[]> delegatesPairingSelector = eaConf
264                                    .getRecombiner();
265                    if (!(delegatesPairingSelector instanceof DuplicatingRecombiner)) {
266                            throw new UnsupportedOperationException(
267                                            "cannot extract offspring from delegate's PairingSelector: "
268                                                            + delegatesPairingSelector);
269                    }
270                    esConf.setOffspring(((DuplicatingRecombiner<double[]>) delegatesPairingSelector)
271                                    .getOffspring());
272    
273                    return esConf;
274            }
275    
276            protected void initConfiguration(
277                            final EaOptimizerConfigurationBase<I> configuration,
278                            final I startIndividual) {
279                    configuration.setPopulationReader(new MemoryPopulationReader<I>(
280                                    startIndividual));
281                    configuration.setPopulationWriter(new NullPopulationWriter<I>());
282    
283                    final Halter<I> halter = createHalter();
284                    reportingHalterWrapper = new ReportingHalterWrapper<I>(halter);
285                    if (halter instanceof CountingHalter) {
286                            addReporter(new LoggingReporter<I>((CountingHalter<I>) halter));
287                    } else {
288                            addReporter(new LoggingReporter<I>(null));
289                    }
290    
291                    configuration.setHalter(reportingHalterWrapper);
292                    addReporter(new TextFileReporter<I>(logFile));
293    
294                    configuration.setParents(parents);
295                    configuration.setPreserveParents(preserveParents);
296            }
297    
298            private Halter<I> createHalter() {
299                    try {
300                            final Long cycles = (Long) commandLine
301                                            .getParsedOptionValue(OPTION_CYCLES);
302                            if (null != cycles) {
303                                    return new CountingHalter<I>(cycles.intValue());
304                            }
305                            final Long ns = (Long) commandLine
306                                            .getParsedOptionValue(OPTION_NS_LIMIT);
307                            if (null != ns) {
308                                    return new ComputingTimeLimitHalter<I>(ns.longValue());
309                            }
310                            final Number limit = (Number) commandLine
311                                            .getParsedOptionValue(OPTION_EVALUATION_LIMIT);
312                            if (null != limit) {
313                                    return new EvaluationLimitHalter<I>(limit.doubleValue());
314                            }
315                            final Date time = (Date) commandLine
316                                            .getParsedOptionValue(OPTION_TIME_LIMIT);
317                            return new TimeBasedHalter<I>(time);
318                    } catch (final ParseException e) {
319                            throw new RuntimeException(e);
320                    }
321            }
322    
323            protected void initConfiguration(
324                            final EaOptimizerConfiguration<I> configuration,
325                            final I startIndividual) {
326                    initConfiguration((EaOptimizerConfigurationBase<I>) configuration,
327                                    startIndividual);
328                    configuration.setRecombiner(new DuplicatingRecombiner<I>(offspring));
329            }
330    
331            @SuppressWarnings("unchecked")
332            protected void initConfiguration(
333                            final EsOptimizerConfiguration configuration,
334                            final I startIndividual) {
335                    initConfiguration((EaOptimizerConfigurationBase<I>) configuration,
336                                    startIndividual);
337                    configuration.setOffspring(offspring);
338            }
339    
340            protected void addReporter(final Reporter<I> reporter) {
341                    reportingHalterWrapper.addReporter(reporter);
342            }
343    
344            private Optimizer<I> createEaOptimizer() throws ParseException {
345                    checkForInvalidMultiplePopulationOption();
346    
347                    final EaOptimizerConfiguration<I> configuration = createEaOptimizerConfiguration();
348    
349                    final String type = commandLine.getOptionValue(OPTION_EA, "simple");
350                    if ("simple".equalsIgnoreCase(type)) {
351                            return new SimpleEaOptimizer<I>(configuration);
352                    }
353                    if ("threaded".equalsIgnoreCase(type)) {
354                            return new ThreadedEaOptimizer<I>(configuration);
355                    }
356                    if ("hadoop".equalsIgnoreCase(type)) {
357                            return new EaHadoOptimizer<I>(configuration);
358                    }
359    
360                    throw new IllegalArgumentException("type not supported: " + type);
361            }
362    
363            private Optimizer<EsIndividual> createEsOptimizer() throws ParseException {
364                    final EsOptimizerConfiguration configuration = createEsOptimizerConfiguration();
365    
366                    final String type = commandLine.getOptionValue(OPTION_ES, "simple");
367                    if ("hadoop".equalsIgnoreCase(type)) {
368                            if (!commandLine.hasOption(OPTION_MULTI_POPULATIONS)) {
369                                    return new EsHadoOptimizer(configuration);
370                            }
371    
372                            Long innerCycleCount = (Long) commandLine
373                                            .getParsedOptionValue(OPTION_MULTI_POPULATIONS);
374                            if (null == innerCycleCount) {
375                                    innerCycleCount = 10l;
376                            }
377                            return createMultiPopulationsHadoopEsOptimizer(configuration,
378                                            innerCycleCount.intValue());
379                    }
380    
381                    checkForInvalidMultiplePopulationOption();
382    
383                    if ("simple".equalsIgnoreCase(type)) {
384                            return new SimpleEsOptimizer(configuration);
385                    }
386                    if ("threaded".equalsIgnoreCase(type)) {
387                            return new ThreadedEsOptimizer(configuration);
388                    }
389    
390                    throw new IllegalArgumentException("type not supported: " + type);
391            }
392    
393            private Optimizer<EsIndividual> createMultiPopulationsHadoopEsOptimizer(
394                            final EsOptimizerConfiguration configuration,
395                            final int innerCycleCount) {
396                    final Halter<EsIndividual> outerHalter = configuration.getHalter();
397                    final CountingHalter<EsIndividual> countingHalter = new CountingHalter<EsIndividual>(
398                                    innerCycleCount);
399                    final ReportingHalterWrapper<EsIndividual> reportingHalterWrapper = new ReportingHalterWrapper<EsIndividual>(
400                                    countingHalter);
401                    reportingHalterWrapper.addReporter(new LoggingReporter<EsIndividual>(
402                                    countingHalter));
403                    configuration.setHalter(reportingHalterWrapper);
404    
405                    return new EsMultiPopulationsHadoOptimizer(configuration, outerHalter);
406            }
407    
408            private void checkForInvalidMultiplePopulationOption() {
409                    if (commandLine.hasOption(OPTION_MULTI_POPULATIONS)) {
410                            throw new IllegalArgumentException(
411                                            "multiPopulation is only allowed with ES-Optimizer of type hadoop");
412                    }
413            }
414    }