import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import net.sf.openrocket.util.BugException;
+
/**
* An implementation of a ParallelFunctionCache that evaluates function values
* in parallel and caches them. This allows pre-calculating possibly required
* function values beforehand. If values are not required after all, the
* computation can be aborted assuming the function evaluation supports it.
+ * <p>
+ * Note that while this class handles threads and abstracts background execution,
+ * the public methods themselves are NOT thread-safe and should be called from
+ * only one thread at a time.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
private Function function;
-
+ /**
+ * Construct a cache that uses the same number of computational threads as there are
+ * processors available.
+ */
public ParallelExecutorCache() {
this(Runtime.getRuntime().availableProcessors());
}
+ /**
+ * Construct a cache that uses the specified number of computational threads for background
+ * computation. The threads that are created are marked as daemon threads.
+ *
+ * @param threadCount the number of threads to use in the executor.
+ */
public ParallelExecutorCache(int threadCount) {
- executor = new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
+ this(new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory() {
@Override
t.setDaemon(true);
return t;
}
- });
+ }));
}
+ /**
+ * Construct a cache that uses the specified ExecutorService for managing
+ * computational threads.
+ *
+ * @param executor the executor to use for function evaluations.
+ */
public ParallelExecutorCache(ExecutorService executor) {
this.executor = executor;
}
- /**
- * Queue a list of function evaluations at the specified points.
- *
- * @param points the points at which to evaluate the function.
- */
+ @Override
public void compute(Collection<Point> points) {
for (Point p : points) {
compute(p);
}
- /**
- * Queue function evaluation for the specified point.
- *
- * @param point the point at which to evaluate the function.
- */
+ @Override
public void compute(Point point) {
+
+ if (isOutsideRange(point)) {
+ // Point is outside of range
+ return;
+ }
+
if (functionCache.containsKey(point)) {
// Function has already been evaluated at the point
return;
return;
}
- double value = function.preComputed(point);
- if (!Double.isNaN(value)) {
- // Function value was in function cache
- functionCache.put(point, value);
- return;
- }
-
// Submit point for evaluation
FunctionCallable callable = new FunctionCallable(function, point);
Future<Double> future = executor.submit(callable);
}
- /**
- * Wait for a collection of points to be computed. After calling this method
- * the function values are available by calling XXX
- *
- * @param points the points to wait for.
- * @throws InterruptedException if this thread was interrupted while waiting.
- */
- public void waitFor(Collection<Point> points) throws InterruptedException {
+ @Override
+ public void waitFor(Collection<Point> points) throws InterruptedException, OptimizationException {
for (Point p : points) {
waitFor(p);
}
}
- /**
- * Wait for a point to be computed. After calling this method
- * the function values are available by calling XXX
- *
- * @param point the point to wait for.
- * @throws InterruptedException if this thread was interrupted while waiting.
- */
- public void waitFor(Point point) throws InterruptedException {
+
+ @Override
+ public void waitFor(Point point) throws InterruptedException, OptimizationException {
+ if (isOutsideRange(point)) {
+ return;
+ }
+
if (functionCache.containsKey(point)) {
return;
}
double value = future.get();
functionCache.put(point, value);
} catch (ExecutionException e) {
- throw new IllegalStateException("Function threw exception while processing", e.getCause());
+ Throwable cause = e.getCause();
+ if (cause instanceof InterruptedException) {
+ throw (InterruptedException) cause;
+ }
+ if (cause instanceof OptimizationException) {
+ throw (OptimizationException) cause;
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+
+ throw new BugException("Function threw unknown exception while processing", e);
}
}
- /**
- * Abort the computation of the specified point. If computation has ended,
- * the result is stored in the function cache anyway.
- *
- * @param points the points to abort.
- * @return a list of the points that have been computed anyway
- */
+
+ @Override
public List<Point> abort(Collection<Point> points) {
List<Point> computed = new ArrayList<Point>(Math.min(points.size(), 10));
}
- /**
- * Abort the computation of the specified point. If computation has ended,
- * the result is stored in the function cache anyway.
- *
- * @param point the point to abort.
- * @return <code>true</code> if the point has been computed anyway, <code>false</code> if not.
- */
+
+ @Override
public boolean abort(Point point) {
+ if (isOutsideRange(point)) {
+ return false;
+ }
+
if (functionCache.containsKey(point)) {
return true;
}
}
+ @Override
public double getValue(Point point) {
+ if (isOutsideRange(point)) {
+ return Double.MAX_VALUE;
+ }
+
Double d = functionCache.get(point);
if (d == null) {
- throw new IllegalStateException(point.toString() + " is not in function cache. " +
+ throw new IllegalStateException(point + " is not in function cache. " +
"functionCache=" + functionCache + " futureMap=" + futureMap);
}
return d;
}
-
@Override
public Function getFunction() {
return function;
functionCache.clear();
}
+
public ExecutorService getExecutor() {
return executor;
}
-
+ /**
+ * Check whether a point is outside of the valid optimization range.
+ */
+ private boolean isOutsideRange(Point p) {
+ int n = p.dim();
+ for (int i = 0; i < n; i++) {
+ double d = p.get(i);
+ // Include NaN in disallowed range
+ if (!(d >= 0.0 && d <= 1.0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
/**
* A Callable that evaluates a function at a specific point and returns the result.
*/
}
@Override
- public Double call() throws InterruptedException {
+ public Double call() throws InterruptedException, OptimizationException {
return calledFunction.evaluate(point);
}
}