/* Copyright 2009-2016 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import org.apache.commons.lang3.reflect.ConstructorUtils; import org.moeaframework.core.EpsilonBoxDominanceArchive; import org.moeaframework.core.FrameworkException; import org.moeaframework.core.NondominatedPopulation; import org.moeaframework.core.PopulationIO; import org.moeaframework.core.Problem; import org.moeaframework.core.comparator.ParetoDominanceComparator; import org.moeaframework.core.spi.ProblemFactory; /** * Provides builder methods for instantiating problems and their reference sets. * This is an internal class with all package-private methods, allowing * subclasses to selectively expose the methods. */ class ProblemBuilder { /** * The problem name. If {@code null}, the problem is specified by * {@code problemClass}. */ String problemName; /** * The problem class. If {@code null}, the problem is specified by * {@code problemName}. */ Class<?> problemClass; /** * The arguments used when constructing an instance of the problem class. * If {@code null}, then the nullary (empty) constructor is used. */ Object[] problemArguments; /** * The problem provider for creating problem instances; or {@code null} * if the default problem factory should be used. */ ProblemFactory problemFactory; /** * The ε values used by this builder. */ double[] epsilon; /** * The file containing the reference set to be used by this builder; or * {@code null} if the reference set should be aggregated from all * individual approximation sets. */ File referenceSetFile; /** * Constructs a new problem builder. */ ProblemBuilder() { super(); } /** * Configures this builder to use the same problem, reference set, ε * values and {@link ProblemFactory} as the specified instance. * * @param builder the instance whose problem, reference set, ε * values and {@code ProblemFactory} are to be copied * @return a reference to this builder */ ProblemBuilder withSameProblemAs(ProblemBuilder builder) { this.problemName = builder.problemName; this.problemClass = builder.problemClass; this.problemArguments = builder.problemArguments; this.problemFactory = builder.problemFactory; this.epsilon = builder.epsilon; this.referenceSetFile = builder.referenceSetFile; return this; } /** * Sets the problem factory used by this builder. * * @param problemFactory the problem factory * @return a reference to this builder */ ProblemBuilder usingProblemFactory(ProblemFactory problemFactory) { this.problemFactory = problemFactory; return this; } /** * Sets the problem used by this builder. * * @param problemName the problem name * @return a reference to this builder */ ProblemBuilder withProblem(String problemName) { this.problemName = problemName; this.problemClass = null; return this; } /** * Sets the problem used by this builder. Instances of the problem are * created using the {@link Class#newInstance()} method. Ensure the problem * class satisfies all the conditions of {@code newInstance()}, otherwise * exceptions may be thrown when attempting to create an instance of the * problem. * * @param problemClass the problem class * @param problemArguments the arguments passed to the problem constructor * @return a reference to this builder */ ProblemBuilder withProblemClass(Class<?> problemClass, Object... problemArguments) { this.problemClass = problemClass; this.problemArguments = problemArguments; this.problemName = null; return this; } /** * Sets the problem used by this builder. Instances of the problem are * created using the {@link Class#newInstance()} method. Ensure the problem * class satisfies all the conditions of {@code newInstance()}, otherwise * exceptions may be thrown when attempting to create an instance of the * problem. * * @param problemClassName the problem class name * @param problemArguments the arguments passed to the problem constructor * @return a reference to this builder * @throws ClassNotFoundException if the specified problem class name could * not be found */ ProblemBuilder withProblemClass(String problemClassName, Object... problemArguments) throws ClassNotFoundException { withProblemClass(Class.forName(problemClassName), problemArguments); return this; } /** * Sets the ε values used by this builder, specifying the archive * returned by {@link #newArchive()}. * * @param epsilon the ε values * @return a reference to this builder */ ProblemBuilder withEpsilon(double... epsilon) { if ((epsilon == null) || (epsilon.length == 0)) { this.epsilon = null; } else { this.epsilon = epsilon; } return this; } /** * Sets the file containing the reference set to be used by this builder. * If not specified, the reference set should be aggregated from all * individual approximation sets. * * @param referenceSetFile the reference set file * @return a reference to this builder */ ProblemBuilder withReferenceSet(File referenceSetFile) { this.referenceSetFile = referenceSetFile; return this; } /** * Returns an empty non-dominated population or ε-box dominance * archive, depending on whether the {@code epsilon} field is set. This is * the archive used to store the reference set. * * @return an empty non-dominated population or ε-box dominance * archive, depending on whether the {@code epsilon} field is set. */ NondominatedPopulation newArchive() { if (epsilon == null) { return new NondominatedPopulation(new ParetoDominanceComparator()); } else { return new EpsilonBoxDominanceArchive(epsilon); } } /** * Returns the reference set used by this builder. The reference set is * generated as follows: * <ol> * <li>If {@link #withReferenceSet(File)} has been set, the contents of * the reference set file are returned; * <li>If the problem factory provides a reference set via the * {@link ProblemFactory#getReferenceSet(String)} method, this * reference set is returned; * <li>Otherwise, an exception is thrown. * </ol> * * @return the reference set used by this builder * @throws IllegalArgumentException if no reference set is available or * could not be loaded */ NondominatedPopulation getReferenceSet() { NondominatedPopulation referenceSet = newArchive(); if (referenceSetFile == null) { //determine if the problem factory provides a reference set NondominatedPopulation factorySet = null; if (problemName != null) { if (problemFactory == null) { factorySet = ProblemFactory.getInstance() .getReferenceSet(problemName); } else { factorySet = problemFactory.getReferenceSet(problemName); } } if (factorySet == null) { throw new IllegalArgumentException( "no reference set available"); } else { referenceSet.addAll(factorySet); } } else { try { referenceSet.addAll(PopulationIO.readObjectives( referenceSetFile)); } catch (IOException e) { throw new IllegalArgumentException( "unable to load reference set", e); } } return referenceSet; } /** * Returns a new instance of the problem used by this builder, or throws * an exception if no problem has been defined. The code requesting the * problem instance is expected to close the problem when finished. * * @return a new instance of the problem used by this builder, or throws * an exception if no problem has been defined * @throws IllegalArgumentException if no problem has been defined * @throws FrameworkException if an error occurred invoking the constructor * caused by an {@link InstantiationException}, * {@link IllegalAccessException}, * {@link InvocationTargetException} or * {@link NoSuchMethodException}. */ Problem getProblemInstance() { if ((problemName == null) && (problemClass == null)) { throw new IllegalArgumentException("no problem specified"); } if (problemClass != null) { try { return (Problem)ConstructorUtils.invokeConstructor(problemClass, problemArguments); } catch (InstantiationException e) { throw new FrameworkException(e); } catch (IllegalAccessException e) { throw new FrameworkException(e); } catch (InvocationTargetException e) { throw new FrameworkException(e); } catch (NoSuchMethodException e) { throw new FrameworkException(e); } } else if (problemFactory == null) { return ProblemFactory.getInstance().getProblem(problemName); } else { return problemFactory.getProblem(problemName); } } }