001/*- 002 ******************************************************************************* 003 * Copyright (c) 2011, 2017 Diamond Light Source Ltd. 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Peter Chang - initial API and implementation and/or initial documentation 011 * Tom Schoonjans - min and max methods 012 *******************************************************************************/ 013 014package org.eclipse.january.dataset; 015 016import java.util.Arrays; 017 018import org.eclipse.january.DatasetException; 019 020/** 021 * Mathematics class for lazy datasets 022 */ 023public final class LazyMaths { 024 025 private LazyMaths() { 026 027 } 028 029 /** 030 * Setup the logging facilities 031 */ 032// private static final Logger logger = LoggerFactory.getLogger(LazyMaths.class); 033 034 // TODO Uncomment this next line when minimum JDK is set to 1.8 035 // @FunctionalInterface 036 private static interface IMathOperation { 037 void execute(IDataset a, IDataset b, Dataset c); 038 } 039 040 private enum MathOperation implements IMathOperation { 041 // TODO use lambdas here when moving to Java 8 042 MAX(new IMathOperation() { 043 @Override 044 public void execute(IDataset a, IDataset b, Dataset c) { 045 Maths.maximum(a, b, c); 046 } 047 }, "maximum"), 048 MIN(new IMathOperation() { 049 @Override 050 public void execute(IDataset a, IDataset b, Dataset c) { 051 Maths.minimum(a, b, c); 052 } 053 }, "minimum"); 054 055 private final IMathOperation operation; 056 private final String operationName; 057 058 private MathOperation(IMathOperation operation, String operationName) { 059 this.operation = operation; 060 this.operationName = operationName; 061 } 062 063 @Override 064 public void execute(IDataset a, IDataset b, Dataset c) { 065 operation.execute(a, b, c); 066 } 067 068 /** 069 * @return the operationName 070 */ 071 public String getOperationName() { 072 return operationName; 073 } 074 075 } 076 077 private static Dataset maxmin(final ILazyDataset data, MathOperation operation, int[] axes) throws DatasetException { 078 // we will be working here with the "ignoreAxes" instead to improve performance dramatically 079 int[] ignoreAxes; 080 if (axes.length == 0) { 081 ignoreAxes = axes; 082 } else { 083 ignoreAxes = ShapeUtils.getRemainingAxes(data.getRank(), axes); 084 } 085 086 final int[] oldShape = data.getShape(); 087 088 SliceND sa = new SliceND(oldShape); 089 SliceNDIterator it = new SliceNDIterator(sa, ignoreAxes); 090 Dataset result = null; 091 092 while (it.hasNext()) { 093 SliceND currentSlice = it.getCurrentSlice(); 094 IDataset slice = data.getSlice(currentSlice); 095 if (result == null) { 096 result = DatasetUtils.convertToDataset(slice); 097 } else { 098 operation.execute(result, slice, result); 099 } 100 } 101 if (result != null) { 102 result.setName(operation.getOperationName()); 103 result.squeeze(); 104 } 105 return result; 106 } 107 108 /** 109 * @param data lazy dataset 110 * @param axes (can be negative). If null or empty then use all axes 111 * @return maximum along axes in lazy dataset 112 * @throws DatasetException when data cannot be retrieved 113 * @since 2.1 114 */ 115 public static Dataset max(final ILazyDataset data, int... axes) throws DatasetException { 116 if (data instanceof Dataset) { 117 Dataset tmp = (Dataset) data; 118 axes = ShapeUtils.checkAxes(data.getRank(), axes); 119 return tmp.max(axes); 120 } 121 return maxmin(data, MathOperation.MAX, axes); 122 } 123 124 /** 125 * @param data lazy dataset 126 * @param axes (can be negative). If null or empty then use all axes 127 * @return minimum along axes in lazy dataset 128 * @throws DatasetException when data cannot be retrieved 129 * @since 2.1 130 */ 131 public static Dataset min(final ILazyDataset data, int... axes) throws DatasetException { 132 if (data instanceof Dataset) { 133 Dataset tmp = (Dataset) data; 134 axes = ShapeUtils.checkAxes(data.getRank(), axes); 135 return tmp.min(axes); 136 } 137 return maxmin(data, MathOperation.MIN, axes); 138 } 139 140 /** 141 * @param data lazy dataset 142 * @param axis (can be negative) 143 * @return sum along axis in lazy dataset 144 * @throws DatasetException when data cannot be retrieved 145 */ 146 public static Dataset sum(final ILazyDataset data, int axis) throws DatasetException { 147 if (data instanceof Dataset) { 148 return ((Dataset) data).sum(axis); 149 } 150 int[][] sliceInfo = new int[3][]; 151 int[] shape = data.getShape(); 152 final Dataset result = prepareDataset(axis, shape, sliceInfo); 153 154 final int[] start = sliceInfo[0]; 155 final int[] stop = sliceInfo[1]; 156 final int[] step = sliceInfo[2]; 157 final int length = shape[axis]; 158 159 for (int i = 0; i < length; i++) { 160 start[axis] = i; 161 stop[axis] = i + 1; 162 result.iadd(data.getSlice(start, stop, step)); 163 } 164 165 result.setShape(ShapeUtils.squeezeShape(shape, axis)); 166 return result; 167 } 168 169 /** 170 * @param data lazy dataset 171 * @param ignoreAxes axes to ignore 172 * @return sum when given axes are ignored in lazy dataset 173 * @throws DatasetException when data cannot be retrieved 174 * @since 2.0 175 */ 176 public static Dataset sum(final ILazyDataset data, int... ignoreAxes) throws DatasetException { 177 return sum(data, true, ignoreAxes); 178 } 179 180 /** 181 * @param data lazy dataset 182 * @param ignore if true, ignore the provided axes, otherwise use only the provided axes 183 * @param axes axes to ignore or accept, depending on the preceding flag 184 * @return sum over all used axes 185 * @throws DatasetException when data cannot be retrieved 186 * @since 2.0 187 */ 188 public static Dataset sum(final ILazyDataset data, boolean ignore, int... axes) throws DatasetException { 189 ILazyDataset rv = data; 190 191 if (ignore) { 192 axes = ShapeUtils.getRemainingAxes(data.getRank(), axes); 193 } else { 194 axes = ShapeUtils.checkAxes(data.getRank(), axes); 195 } 196 for (int i = 0 ; i < axes.length ; i++) { 197 rv = sum(rv, axes[i] - i); 198 } 199 200 return DatasetUtils.sliceAndConvertLazyDataset(rv); 201 } 202 203 /** 204 * @param data lazy dataset 205 * @param axis (can be negative) 206 * @return product along axis in lazy dataset 207 * @throws DatasetException when data cannot be retrieved 208 */ 209 public static Dataset product(final ILazyDataset data, int axis) throws DatasetException { 210 int[][] sliceInfo = new int[3][]; 211 int[] shape = data.getShape(); 212 final Dataset result = prepareDataset(axis, shape, sliceInfo); 213 result.fill(1); 214 215 final int[] start = sliceInfo[0]; 216 final int[] stop = sliceInfo[1]; 217 final int[] step = sliceInfo[2]; 218 final int length = shape[axis]; 219 220 for (int i = 0; i < length; i++) { 221 start[axis] = i; 222 stop[axis] = i + 1; 223 result.imultiply(data.getSlice(start, stop, step)); 224 } 225 226 result.setShape(ShapeUtils.squeezeShape(shape, axis)); 227 return result; 228 } 229 230 /** 231 * @param start number to begin at 232 * @param stop inclusive 233 * @param data lazy dataset 234 * @param ignoreAxes axes to ignore 235 * @return mean when given axes are ignored in lazy dataset 236 * @throws DatasetException when data cannot be retrieved 237 */ 238 public static Dataset mean(int start, int stop, ILazyDataset data, int... ignoreAxes) throws DatasetException { 239 int[] shape = data.getShape(); 240 PositionIterator iter = new PositionIterator(shape, ignoreAxes); 241 int[] pos = iter.getPos(); 242 boolean[] omit = iter.getOmit(); 243 244 int rank = shape.length; 245 int[] st = new int[rank]; 246 Arrays.fill(st, 1); 247 int[] end = new int[rank]; 248 249 RunningAverage av = null; 250 int c = 0; 251 while (iter.hasNext() && c < stop) { 252 if (c++ < start) continue; 253 for (int i = 0; i < rank; i++) { 254 end[i] = omit[i] ? shape[i] : pos[i] + 1; 255 } 256 IDataset ds = data.getSlice(pos, end, st); 257 if (av == null) { 258 av = new RunningAverage(ds); 259 } else { 260 av.update(ds); 261 } 262 } 263 264 return av != null ? av.getCurrentAverage().squeeze() : null; 265 } 266 267 /** 268 * @param data lazy dataset 269 * @param ignoreAxes axes to ignore 270 * @return mean when given axes are ignored in lazy dataset 271 * @throws DatasetException when data cannot be retrieved 272 */ 273 public static Dataset mean(ILazyDataset data, int... ignoreAxes) throws DatasetException { 274 return mean(0, Integer.MAX_VALUE -1 , data, ignoreAxes); 275 } 276 277 private static Dataset prepareDataset(int axis, int[] shape, int[][] sliceInfo) { 278 int rank = shape.length; 279 axis = ShapeUtils.checkAxis(rank, axis); 280 281 sliceInfo[0] = new int[rank]; 282 sliceInfo[1] = shape.clone(); 283 sliceInfo[2] = new int[rank]; 284 Arrays.fill(sliceInfo[2], 1); 285 286 final int[] nshape = shape.clone(); 287 nshape[axis] = 1; 288 289 return DatasetFactory.zeros(nshape); 290 } 291}