/*-
* Copyright 2015 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.operations;
import java.util.Arrays;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DatasetUtils;
import org.eclipse.january.dataset.DoubleDataset;
import org.eclipse.january.dataset.IndexIterator;
import uk.ac.diamond.scisoft.analysis.utils.SimpleUncertaintyPropagationMath;
/**
* A class providing error propagating static methods
*/
public class ErrorPropagationUtils {
/**
* Adds a scalar to a Dataset, propagating uncertainties.
* <p>
* If the Dataset operand has uncertainty (error) metadata, then the errors
* are correctly propagated to the uncertainty metadata of the resultant
* Dataset.
* @param a
* Dataset operand
* @param b
* scalar operand
* @return Dataset of the sum of a and b, with correctly propagated
* uncertainties.
*/
public static DoubleDataset addWithUncertainty(Dataset a, Dataset b) {
return operateWithUncertainty(a, b, new Add());
}
/**
* Subtracts a scalar from a Dataset, propagating uncertainties.
* <p>
* If the Dataset operand has uncertainty (error) metadata, then the errors
* are correctly propagated to the uncertainty metadata of the resultant
* Dataset.
* @param a
* Dataset operand
* @param b
* scalar operand
* @return Dataset of the difference of a and b, with correctly propagated
* uncertainties.
*/
public static DoubleDataset subtractWithUncertainty(Dataset a, Dataset b) {
return operateWithUncertainty(a, b, new Subtract());
}
/**
* Multiplies a Dataset by a scalar, propagating uncertainties.
* <p>
* If the Dataset operand has uncertainty (error) metadata, then the errors
* are correctly propagated to the uncertainty metadata of the resultant
* Dataset.
* @param a
* Dataset operand
* @param b
* scalar operand
* @return Dataset of the product of a and b, with correctly propagated
* uncertainties.
*/
public static DoubleDataset multiplyWithUncertainty(Dataset a, Dataset b) {
return operateWithUncertainty(a, b, new Multiply());
}
/**
* Divides a Dataset by a scalar, propagating uncertainties.
* <p>
* If the Dataset operand has uncertainty (error) metadata, then the errors
* are correctly propagated to the uncertainty metadata of the resultant
* Dataset.
* @param a
* Dataset operand
* @param b
* scalar or dataset operand
* @return Dataset of the ratio of a and b, with correctly propagated
* uncertainties.
*/
public static DoubleDataset divideWithUncertainty(Dataset a, Dataset b) {
return operateWithUncertainty(a, b, new Divide());
}
private static DoubleDataset operateWithUncertainty(Dataset input, Dataset oprahend, UncertaintyOperator operator) {
if (oprahend.getSize() != 1 && input.getSize() != oprahend.getSize()) throw new IllegalArgumentException("Cannot process datasets of these shapes!");
Dataset inputUncert = input.getErrors();
Dataset oprahendUncert = oprahend.getErrors();
DoubleDataset output = (DoubleDataset) DatasetFactory.zeros(input, Dataset.FLOAT64);
DoubleDataset outputUncert = (inputUncert == null) ? null : (DoubleDataset) DatasetFactory.zeros(inputUncert, Dataset.FLOAT64);
//assume data and errors are either not views or common views
IndexIterator iter = input.getIterator();
if (oprahend.getSize() == 1) {
double val = oprahend.getDouble();
if (inputUncert != null) {
double[] out = new double[2];
while(iter.hasNext()) {
operator.operate(input.getElementDoubleAbs(iter.index), val, inputUncert.getElementDoubleAbs(iter.index), out);
output.setAbs(iter.index, out[0]);
outputUncert.setAbs(iter.index, out[1]);
}
output.setErrors(outputUncert);
} else {
while (iter.hasNext())
output.setAbs(iter.index, operator.operate(input.getElementDoubleAbs(iter.index), val));
}
return output;
}
if (inputUncert != null && oprahendUncert == null) {
double[] out = new double[2];
while(iter.hasNext()) {
operator.operate(input.getElementDoubleAbs(iter.index), oprahend.getElementDoubleAbs(iter.index), inputUncert.getElementDoubleAbs(iter.index), out);
output.setAbs(iter.index, out[0]);
outputUncert.setAbs(iter.index, out[1]);
}
output.setErrors(outputUncert);
} else if (inputUncert != null && oprahendUncert != null) {
double[] out = new double[2];
while(iter.hasNext()) {
operator.operate(input.getElementDoubleAbs(iter.index), oprahend.getElementDoubleAbs(iter.index), inputUncert.getElementDoubleAbs(iter.index),oprahendUncert.getElementDoubleAbs(iter.index), out);
output.setAbs(iter.index, out[0]);
outputUncert.setAbs(iter.index, out[1]);
}
output.setErrors(outputUncert);
} else {
while (iter.hasNext()){
output.setAbs(iter.index, operator.operate(input.getElementDoubleAbs(iter.index), oprahend.getElementDoubleAbs(iter.index)));
}
}
return output;
}
}
interface UncertaintyOperator {
void operate(double a, double b, double ae, double be, double[] out);
void operate(double a, double b, double ae, double[] out);
double operate(double a, double b);
}
class Add implements UncertaintyOperator {
@Override
public void operate(double a, double b, double ae, double be, double[] out) {
SimpleUncertaintyPropagationMath.add(a, b, ae, be, out);
}
@Override
public void operate(double a, double b, double ae, double[] out) {
out[0] = a+b;
out[1] = ae;
}
@Override
public double operate(double a, double b) {
return a+b;
}
}
class Subtract implements UncertaintyOperator {
@Override
public void operate(double a, double b, double ae, double be, double[] out) {
SimpleUncertaintyPropagationMath.subtract(a, b, ae, be, out);
}
@Override
public void operate(double a, double b, double ae, double[] out) {
out[0] = a-b;
out[1] = ae;
}
@Override
public double operate(double a, double b) {
return a-b;
}
}
class Multiply implements UncertaintyOperator {
@Override
public void operate(double a, double b, double ae, double be, double[] out) {
SimpleUncertaintyPropagationMath.multiply(a, b, ae, be, out);
}
@Override
public void operate(double a, double b, double ae, double[] out) {
SimpleUncertaintyPropagationMath.multiply(a, b, ae, out);
}
@Override
public double operate(double a, double b) {
return a*b;
}
}
class Divide implements UncertaintyOperator {
@Override
public void operate(double a, double b, double ae, double be, double[] out) {
SimpleUncertaintyPropagationMath.divide(a, b, ae, be, out);
}
@Override
public void operate(double a, double b, double ae, double[] out) {
SimpleUncertaintyPropagationMath.divide(a, b, ae, out);
}
@Override
public double operate(double a, double b) {
return a/b;
}
}