1 package net.sf.openrocket.optimization.general;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashMap;
8 import java.util.concurrent.Callable;
9 import java.util.concurrent.ExecutionException;
10 import java.util.concurrent.ExecutorService;
11 import java.util.concurrent.Future;
12 import java.util.concurrent.LinkedBlockingQueue;
13 import java.util.concurrent.ThreadFactory;
14 import java.util.concurrent.ThreadPoolExecutor;
15 import java.util.concurrent.TimeUnit;
18 * An implementation of a ParallelFunctionCache that evaluates function values
19 * in parallel and caches them. This allows pre-calculating possibly required
20 * function values beforehand. If values are not required after all, the
21 * computation can be aborted assuming the function evaluation supports it.
23 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
25 public class ParallelExecutorCache implements ParallelFunctionCache {
27 private final Map<Point, Double> functionCache = new HashMap<Point, Double>();
28 private final Map<Point, Future<Double>> futureMap = new HashMap<Point, Future<Double>>();
30 private ExecutorService executor;
32 private Function function;
36 public ParallelExecutorCache() {
37 this(Runtime.getRuntime().availableProcessors());
40 public ParallelExecutorCache(int threadCount) {
41 executor = new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
42 new LinkedBlockingQueue<Runnable>(),
45 public Thread newThread(Runnable r) {
46 Thread t = new Thread(r);
53 public ParallelExecutorCache(ExecutorService executor) {
54 this.executor = executor;
60 * Queue a list of function evaluations at the specified points.
62 * @param points the points at which to evaluate the function.
64 public void compute(Collection<Point> points) {
65 for (Point p : points) {
72 * Queue function evaluation for the specified point.
74 * @param point the point at which to evaluate the function.
76 public void compute(Point point) {
77 if (functionCache.containsKey(point)) {
78 // Function has already been evaluated at the point
82 if (futureMap.containsKey(point)) {
83 // Function is being evaluated at the point
87 double value = function.preComputed(point);
88 if (!Double.isNaN(value)) {
89 // Function value was in function cache
90 functionCache.put(point, value);
94 // Submit point for evaluation
95 FunctionCallable callable = new FunctionCallable(function, point);
96 Future<Double> future = executor.submit(callable);
97 futureMap.put(point, future);
102 * Wait for a collection of points to be computed. After calling this method
103 * the function values are available by calling XXX
105 * @param points the points to wait for.
106 * @throws InterruptedException if this thread was interrupted while waiting.
108 public void waitFor(Collection<Point> points) throws InterruptedException {
109 for (Point p : points) {
115 * Wait for a point to be computed. After calling this method
116 * the function values are available by calling XXX
118 * @param point the point to wait for.
119 * @throws InterruptedException if this thread was interrupted while waiting.
121 public void waitFor(Point point) throws InterruptedException {
122 if (functionCache.containsKey(point)) {
126 Future<Double> future = futureMap.get(point);
127 if (future == null) {
128 throw new IllegalStateException("waitFor called for " + point + " but it is not being computed");
132 double value = future.get();
133 functionCache.put(point, value);
134 } catch (ExecutionException e) {
135 throw new IllegalStateException("Function threw exception while processing", e.getCause());
141 * Abort the computation of the specified point. If computation has ended,
142 * the result is stored in the function cache anyway.
144 * @param points the points to abort.
145 * @return a list of the points that have been computed anyway
147 public List<Point> abort(Collection<Point> points) {
148 List<Point> computed = new ArrayList<Point>(Math.min(points.size(), 10));
150 for (Point p : points) {
161 * Abort the computation of the specified point. If computation has ended,
162 * the result is stored in the function cache anyway.
164 * @param point the point to abort.
165 * @return <code>true</code> if the point has been computed anyway, <code>false</code> if not.
167 public boolean abort(Point point) {
168 if (functionCache.containsKey(point)) {
172 Future<Double> future = futureMap.remove(point);
173 if (future == null) {
174 throw new IllegalStateException("abort called for " + point + " but it is not being computed");
177 if (future.isDone()) {
178 // Evaluation has been completed, store value in cache
180 double value = future.get();
181 functionCache.put(point, value);
183 } catch (Exception e) {
187 // Cancel the evaluation
194 public double getValue(Point point) {
195 Double d = functionCache.get(point);
197 throw new IllegalStateException(point.toString() + " is not in function cache. " +
198 "functionCache=" + functionCache + " futureMap=" + futureMap);
206 public Function getFunction() {
211 public void setFunction(Function function) {
212 this.function = function;
217 public void clearCache() {
218 List<Point> list = new ArrayList<Point>(futureMap.keySet());
220 functionCache.clear();
223 public ExecutorService getExecutor() {
230 * A Callable that evaluates a function at a specific point and returns the result.
232 private class FunctionCallable implements Callable<Double> {
233 private final Function calledFunction;
234 private final Point point;
236 public FunctionCallable(Function function, Point point) {
237 this.calledFunction = function;
242 public Double call() throws InterruptedException {
243 return calledFunction.evaluate(point);