    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}