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