/* 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.util.distributed; import java.io.Serializable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.moeaframework.core.Problem; import org.moeaframework.core.Solution; import org.moeaframework.problem.ProblemException; /** * Distributes the {@link #evaluate(Solution)} method across multiple threads, * cores or compute nodes using the provided {@link ExecutorService}. The * {@code ExecutorService} defines the type and method of distribution. The * problem must be {@link Serializable} if executing on remote nodes. */ public class DistributedProblem implements Problem { /** * The {@code ExecutorService} for distributing jobs across multiple * threads, cores or compute nodes. */ private final ExecutorService executor; /** * The problem. */ private final Problem innerProblem; /** * Decorates a problem for distributing the evaluation of the problem across * multiple threads, cores or compute nodes as defined by the specified * {@code ExecutorService}. * * @param problem the problem being distributed * @param executor the {@code ExecutorService} for distributing jobs across * multiple threads, cores or compute nodes */ public DistributedProblem(Problem problem, ExecutorService executor) { super(); this.innerProblem = problem; this.executor = executor; } /** * The {@link Callable} sent to the {@code ExecutorService} for distributed * processing. Note that serialization may result in the solution being * evaluated and returned may be a different instance than provided to the * constructor. It is therefore necessary to ensure the required fields are * copied when appropriate. */ private static class ProblemEvaluator implements Callable<Solution>, Serializable { private static final long serialVersionUID = -4812427470992224532L; /** * The problem. */ private final Problem problem; /** * The solution being evaluated. */ private final Solution solution; /** * Constructs a distributed job to evaluate the specified solution. * * @param problem the problem * @param solution the solution to be evaluated */ public ProblemEvaluator(Problem problem, Solution solution) { super(); this.problem = problem; this.solution = solution; } @Override public Solution call() throws Exception { problem.evaluate(solution); return solution; } } @Override public void evaluate(Solution solution) { if (solution instanceof FutureSolution) { FutureSolution futureSolution = (FutureSolution)solution; Future<Solution> future = executor.submit(new ProblemEvaluator( innerProblem, futureSolution)); futureSolution.setFuture(future); } else { throw new ProblemException(this, "requires FutureSolution"); } } @Override public String getName() { return innerProblem.getName(); } @Override public int getNumberOfConstraints() { return innerProblem.getNumberOfConstraints(); } @Override public int getNumberOfObjectives() { return innerProblem.getNumberOfObjectives(); } @Override public int getNumberOfVariables() { return innerProblem.getNumberOfVariables(); } @Override public Solution newSolution() { return new FutureSolution(innerProblem.newSolution()); } @Override public void close() { innerProblem.close(); } }