/*
* Copyright (c) 2012 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.processing.test;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.eclipse.dawnsci.analysis.api.fitting.functions.IFunction;
import org.eclipse.dawnsci.analysis.api.processing.ExecutionType;
import org.eclipse.dawnsci.analysis.api.processing.IExecutionVisitor;
import org.eclipse.dawnsci.analysis.api.processing.IOperation;
import org.eclipse.dawnsci.analysis.api.processing.IOperationContext;
import org.eclipse.dawnsci.analysis.api.processing.IOperationService;
import org.eclipse.dawnsci.analysis.api.processing.OperationData;
import org.eclipse.january.IMonitor;
import org.eclipse.january.dataset.AggregateDataset;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DoubleDataset;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.Maths;
import org.junit.BeforeClass;
import org.junit.Test;
import uk.ac.diamond.scisoft.analysis.fitting.functions.CompositeFunction;
import uk.ac.diamond.scisoft.analysis.fitting.functions.FunctionFactory;
import uk.ac.diamond.scisoft.analysis.fitting.functions.Gaussian;
import uk.ac.diamond.scisoft.analysis.fitting.functions.Polynomial;
import uk.ac.diamond.scisoft.analysis.fitting.functions.PseudoVoigt;
import uk.ac.diamond.scisoft.analysis.optimize.GeneticAlg;
import uk.ac.diamond.scisoft.analysis.processing.Activator;
import uk.ac.diamond.scisoft.analysis.processing.operations.FittingModel;
import uk.ac.diamond.scisoft.analysis.processing.operations.FunctionModel;
import uk.ac.diamond.scisoft.analysis.processing.runner.OperationRunnerImpl;
import uk.ac.diamond.scisoft.analysis.processing.runner.SeriesRunner;
public class FunctionsTest {
private static IOperationService service;
/**
* Manually creates the service so that no extension points have to be read.
*
* We do this use annotations
* @throws Exception
*/
@BeforeClass
public static void before() throws Exception {
service = (IOperationService)Activator.getService(IOperationService.class);
// Just read all these operations.
service.createOperations(service.getClass().getClassLoader(), "uk.ac.diamond.scisoft.analysis.processing.operations");
OperationRunnerImpl.setRunner(ExecutionType.SERIES, new SeriesRunner());
OperationRunnerImpl.setRunner(ExecutionType.PARALLEL, new SeriesRunner());
/*FunctionFactory has been set up as an OSGI service so need to register
*function before it is called (or make this a JUnit PluginTest.
*/
FunctionFactory.registerFunction(Polynomial.class, true);
FunctionFactory.registerFunction(PseudoVoigt.class, true);
}
@Test
public void testPolynomial() throws Exception {
final IDataset indices = DatasetFactory.createRange(1000, Dataset.INT);
final IOperationContext context = service.createContext();
context.setData(indices);
context.setDataDimensions(new int[]{0});
final IOperation functionOp = service.findFirst("uk.ac.diamond.scisoft.analysis.processing.operations.functionOperation");
// y(x) = a_0 x^n + a_1 x^(n-1) + a_2 x^(n-2) + ... + a_(n-1) x + a_n
final IFunction poly = FunctionFactory.getFunction("Polynomial", 3/*x^2*/, 5.3/*x*/, 9.4/*m*/);
functionOp.setModel(new FunctionModel(poly));
context.setVisitor(new IExecutionVisitor.Stub() {
@Override
public void executed(OperationData result, IMonitor monitor) throws Exception {
System.out.println(result.getData().getName());
for (int i = 0; i < result.getData().getShape()[0]; i++) {
// Get result
double opVal = result.getData().getDouble(i);
// Calc polynomial
double v = poly.getParameterValue(0);
double p = indices.getDouble(i);
for (int j = 1; j < poly.getNoOfParameters(); j++) {
v = v * p + poly.getParameterValue(j);
}
if (opVal != v) {
throw new Exception("Did not process polynomial function correctly! ");
}
}
}
});
context.setSeries(functionOp);
service.execute(context);
context.setExecutionType(ExecutionType.PARALLEL);
service.execute(context);
}
static final int[] defaultPeakPos;
static {
int[] tmp = new int[] { 100, 200, 300, 400, 500, 150, 250, 350, 450 };
Arrays.sort(tmp);
defaultPeakPos = tmp;
}
static final int defaultFWHM = 20;
static final int defaultArea = 50;
static final int dataRange = 550;
static final DoubleDataset xAxis = (DoubleDataset) DatasetFactory.createRange(0, dataRange, 1, Dataset.FLOAT64);
static final boolean backgroundDominated = true;
static final boolean autoStopping = true;
static final double threshold = 0.10;
static final int numPeaks = -1;
static final int smoothing = 5;
static final long seed = 12357L;
private volatile int count;
@Test
public void testPseudoVoigtGenetic() throws Exception {
final IOperation fittingOp = service.findFirst("uk.ac.diamond.scisoft.analysis.processing.operations.fittingOperation");
// We do 10 Peak fits
final Dataset pseudo = generatePseudoVoigt(defaultPeakPos.length);
final AggregateDataset aggy = new AggregateDataset(true, pseudo, pseudo, pseudo, pseudo, pseudo);
final IOperationContext context = service.createContext();
context.setData(aggy);
context.setDataDimensions(new int[]{1});
// Cannot send a concrete GeneticAlg here because does not work in parallel.
fittingOp.setModel(new FittingModel(xAxis, FunctionFactory.getPeakFunctionClass("PseudoVoigt"), GeneticAlg.class, 0.0001, seed, smoothing, numPeaks, threshold, autoStopping, backgroundDominated));
count = 0;
context.setVisitor(new IExecutionVisitor.Stub() {
@Override
public void executed(OperationData result, IMonitor monitor) throws Exception {
System.out.println(result.getData().getName());
final List<CompositeFunction> fittedPeakList = (List<CompositeFunction>)result.getAuxData()[0];
double[] fittedPeakPos = new double[fittedPeakList.size()];
int i = 0;
for (CompositeFunction p : fittedPeakList) {
fittedPeakPos[i++] = p.getPeak(0).getPosition();
}
Arrays.sort(fittedPeakPos);
assertEquals("The number of peaks found was not the same as generated", defaultPeakPos.length, fittedPeakPos.length);
for (int k = 0; k < fittedPeakPos.length; k++) {
assertEquals(defaultPeakPos[k], fittedPeakPos[k], 2d);
}
count++;
}
});
context.setSeries(fittingOp);
service.execute(context);
if (count!=5) throw new Exception("Tiled 10x"+dataRange+" did not fit ten times!");
count = 0;
context.setExecutionType(ExecutionType.PARALLEL);
service.execute(context);
if (count!=5) throw new Exception("Tiled 10x"+dataRange+" did not fit ten times!");
}
@Test
public void testPseudoVoigtGeneticParallel() throws Exception {
final IOperation fittingOp = service.findFirst("uk.ac.diamond.scisoft.analysis.processing.operations.fittingOperation");
// We do 10 Peak fits
final Dataset pseudo = generatePseudoVoigt(defaultPeakPos.length);
final AggregateDataset aggy = new AggregateDataset(true, pseudo, pseudo, pseudo, pseudo, pseudo);
final IOperationContext context = service.createContext();
context.setData(aggy);
context.setDataDimensions(new int[]{1});
// Cannot send a concrete GeneticAlg here because does not work in parallel.
fittingOp.setModel(new FittingModel(xAxis, PseudoVoigt.class, GeneticAlg.class, 0.0001, seed, smoothing, numPeaks, threshold, autoStopping, backgroundDominated));
count = 0;
context.setParallelTimeout(Long.MAX_VALUE);
context.setVisitor(new IExecutionVisitor.Stub() {
@Override
public void executed(OperationData result, IMonitor monitor) throws Exception {
System.out.println(result.getData().getName());
final List<CompositeFunction> fittedPeakList = (List<CompositeFunction>)result.getAuxData()[0];
double[] fittedPeakPos = new double[fittedPeakList.size()];
int i = 0;
for (CompositeFunction p : fittedPeakList) {
fittedPeakPos[i++] = p.getPeak(0).getPosition();
}
Arrays.sort(fittedPeakPos);
assertEquals("The number of peaks found was not the same as generated", defaultPeakPos.length, fittedPeakPos.length);
for (int k = 0; k < fittedPeakPos.length; k++) {
assertEquals(defaultPeakPos[k], fittedPeakPos[k], 2d);
}
try {
// This sleep simply introduces some random behaviour
// on the parallel jobs so that we definitely get a different order.
final long time = Math.round(Math.random()*1000);
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
});
context.setSeries(fittingOp);
context.setExecutionType(ExecutionType.PARALLEL);
service.execute(context);
Thread.sleep(1000);
if (count!=5) throw new Exception("Tiled 10x"+dataRange+" did not fit 5 times! "+count);
}
private DoubleDataset generatePseudoVoigt(int numPeaks) {
CompositeFunction function = new CompositeFunction();
if (numPeaks > defaultPeakPos.length)
numPeaks = defaultPeakPos.length;
for (int i = 0; i < numPeaks; i++) {
function.addFunction(new PseudoVoigt(defaultPeakPos[i] - 20, defaultPeakPos[i] + 20, defaultFWHM,
defaultArea));
}
DoubleDataset data = function.calculateValues(xAxis);
return (DoubleDataset) Maths.add(data, generateBackground());
}
private DoubleDataset generateBackground() {
CompositeFunction comp = new CompositeFunction();
comp.addFunction(new Gaussian(-10, 10, dataRange / 4, dataRange / 2));
return comp.calculateValues(DatasetFactory.createRange(DoubleDataset.class, dataRange));
}
}