001/*-
002 *******************************************************************************
003 * Copyright (c) 2011, 2016 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 *******************************************************************************/
012
013package org.eclipse.january.dataset;
014
015import java.util.ArrayList;
016import java.util.Arrays;
017import java.util.List;
018
019import org.eclipse.january.DatasetException;
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023/**
024 * Mathematics class for lazy datasets
025 */
026public final class LazyMaths {
027        /**
028         * Setup the logging facilities
029         */
030        protected static final Logger logger = LoggerFactory.getLogger(LazyMaths.class);
031
032        /**
033         * @param data
034         * @param axis (can be negative)
035         * @return sum along axis in lazy dataset
036         * @throws DatasetException 
037         */
038        public static Dataset sum(final ILazyDataset data, int axis) throws DatasetException {
039                if (data instanceof Dataset)
040                        return ((Dataset) data).sum(axis);
041                int[][] sliceInfo = new int[3][];
042                int[] shape = data.getShape();
043                final Dataset result = prepareDataset(axis, shape, sliceInfo);
044
045                final int[] start = sliceInfo[0];
046                final int[] stop = sliceInfo[1];
047                final int[] step = sliceInfo[2];
048                final int length = shape[axis];
049
050                for (int i = 0; i < length; i++) {
051                        start[axis] = i;
052                        stop[axis] = i + 1;
053                        result.iadd(data.getSlice(start, stop, step));
054                }
055
056                result.setShape(ShapeUtils.squeezeShape(shape, axis));
057                return result;
058        }
059
060        /**
061         * @param data
062         * @param ignoreAxes axes to ignore
063         * @return sum when given axes are ignored in lazy dataset
064         * @throws DatasetException 
065         * @since 2.0
066         */
067        public static Dataset sum(final ILazyDataset data, int... ignoreAxes) throws DatasetException {
068                return sum(data, true, ignoreAxes);
069        }
070        
071        /**
072         * @param data
073         * @param ignore if true, ignore the provided axes, otherwise use only the provided axes 
074         * @param axes axes to ignore or accept, depending on the preceding flag
075         * @return sum
076         * @throws DatasetException 
077         * @since 2.0
078         */
079        public static Dataset sum(final ILazyDataset data, boolean ignore, int... axes) throws DatasetException {
080                Arrays.sort(axes); // ensure they are properly sorted
081        
082                ILazyDataset rv = data;
083                
084                if (ignore) {
085                        List<Integer> goodAxes = new ArrayList<Integer>();
086                        for (int i = 0 ; i < data.getRank() ; i++) {
087                                boolean found = false;
088                                for (int j = 0 ; j < axes.length ; j++) {
089                                        if (i == axes[j]) {
090                                                found = true;
091                                                break;
092                                        }
093                                }
094                                if (!found)             
095                                        goodAxes.add(i);
096                        }
097
098                        for (int i = 0 ; i < goodAxes.size() ; i++) {
099                                rv = sum(rv, goodAxes.get(i) - i);
100                        }
101                } else {
102                        for (int i = 0 ; i < axes.length ; i++) {
103                                rv = sum(rv, axes[i] - i);
104                        }
105                }
106                return DatasetUtils.sliceAndConvertLazyDataset(rv);
107        }
108        
109        /**
110         * @param data
111         * @param axis (can be negative)
112         * @return product along axis in lazy dataset
113         * @throws DatasetException 
114         */
115        public static Dataset product(final ILazyDataset data, int axis) throws DatasetException {
116                int[][] sliceInfo = new int[3][];
117                int[] shape = data.getShape();
118                final Dataset result = prepareDataset(axis, shape, sliceInfo);
119                result.fill(1);
120
121                final int[] start = sliceInfo[0];
122                final int[] stop = sliceInfo[1];
123                final int[] step = sliceInfo[2];
124                final int length = shape[axis];
125
126                for (int i = 0; i < length; i++) {
127                        start[axis] = i;
128                        stop[axis] = i + 1;
129                        result.imultiply(data.getSlice(start, stop, step));
130                }
131
132                result.setShape(ShapeUtils.squeezeShape(shape, axis));
133                return result;
134        }
135
136        /**
137         * @param start
138         * @param stop inclusive
139         * @param data
140         * @param ignoreAxes
141         * @return mean when given axes are ignored in lazy dataset
142         * @throws DatasetException 
143         */
144        public static Dataset mean(int start, int stop, ILazyDataset data, int... ignoreAxes) throws DatasetException {
145                int[] shape = data.getShape();
146                PositionIterator iter = new PositionIterator(shape, ignoreAxes);
147                int[] pos = iter.getPos();
148                boolean[] omit = iter.getOmit();
149
150                int rank = shape.length;
151                int[] st = new int[rank];
152                Arrays.fill(st, 1);
153                int[] end = new int[rank];
154
155                RunningAverage av = null;
156                int c = 0;
157                while (iter.hasNext() && c < stop) {
158                        if (c++ < start) continue;
159                        for (int i = 0; i < rank; i++) {
160                                end[i] = omit[i] ? shape[i] : pos[i] + 1;
161                        }
162                        IDataset ds = data.getSlice(pos, end, st);
163                        if (av == null) {
164                                av = new RunningAverage(ds);
165                        } else {
166                                av.update(ds);
167                        }
168                }
169
170                return  av != null ? av.getCurrentAverage().squeeze() : null;
171        }
172        
173        public static Dataset mean(ILazyDataset data, int... ignoreAxes) throws DatasetException {
174                return mean(0, Integer.MAX_VALUE -1 , data, ignoreAxes);
175        }
176
177        @SuppressWarnings("deprecation")
178        private static Dataset prepareDataset(int axis, int[] shape, int[][] sliceInfo) {
179                int rank = shape.length;
180                if (axis < 0)
181                        axis += rank;
182                if (axis < 0 || axis >= rank) {
183                        logger.error("Axis argument is outside allowed range");
184                        throw new IllegalArgumentException("Axis argument is outside allowed range");
185                }
186
187                sliceInfo[0] = new int[rank];
188                sliceInfo[1] = shape.clone();
189                sliceInfo[2] = new int[rank];
190                Arrays.fill(sliceInfo[2], 1);
191
192                final int[] nshape = shape.clone();
193                nshape[axis] = 1;
194
195                return DatasetFactory.zeros(nshape, Dataset.FLOAT64);
196        }
197}