com.meliorbis.numerics.index.impl.Index Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Numerics Show documentation
Show all versions of Numerics Show documentation
A library for working with large multi-dimensional arrays and the functions they represent
package com.meliorbis.numerics.index.impl;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.function.IntConsumer;
import org.apache.commons.lang.ArrayUtils;
import com.meliorbis.numerics.NumericsException;
import com.meliorbis.numerics.index.IndexIterator;
import com.meliorbis.numerics.index.SkippingLinearIterator;
import com.meliorbis.numerics.index.SubIndexIterator;
import com.meliorbis.utils.Pair;
import com.meliorbis.utils.Utils;
/**
* A multi-dimensional index that can convert to/from linear indexing and provides
* iterators, including separate simultaneous iterators on different sub-spaces
*
* @author Tobias Grasl
*/
public class Index implements Iterable, com.meliorbis.numerics.index.Index
{
private static final boolean FORCE_CORRECT_DIMS = Boolean.getBoolean("com.meliorbis.numerics.forceCorrectDims");
private final int[] _sizes;
public final int[] _stepSizes;
private int _numberOfElements;
public Index(int... sizes_)
{
_sizes = Arrays.copyOf(sizes_,sizes_.length);
// Create an array to hold the size of the subspace beyond each dimension
_stepSizes = new int[getSizes().length];
// The last dimensions stepsize is just one
_stepSizes[getSizes().length - 1] = 1;
// each previous one is the product of the next dimensions size*times it's stepsize
// (effectively the reverse cumulative product)
for(int index = getSizes().length-1; index-- > 0;)
{
_stepSizes[index] = _stepSizes[index+1]*getSizes()[index+1];
}
// The total number of elements
_numberOfElements = _stepSizes[0]*_sizes[0];
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#getSizes()
*/
@Override
public int[] getSizes()
{
return _sizes;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#numberOfElements()
*/
@Override
public int numberOfElements()
{
return _numberOfElements;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#numberOfDimensions()
*/
@Override
public int numberOfDimensions()
{
return _sizes.length;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#toLinearIndex(int)
*/
@Override
public int toLinearIndex(int... logicalIndex_) throws IndexOutOfBoundsException
{
int linearIndex = 0;
if(logicalIndex_.length != numberOfDimensions())
{
throw new IndexOutOfBoundsException(
String.format("The index has %s dimensions, but %s index elements were passed",numberOfDimensions(), logicalIndex_.length));
}
for (int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
{
if(logicalIndex_[dimIndex] >= _sizes[dimIndex])
{
throw new IndexOutOfBoundsException(
String.format("Dimension %s is of size %s, but element %s was requested", dimIndex, _sizes[dimIndex], logicalIndex_[dimIndex]));
}
linearIndex += logicalIndex_[dimIndex]*_stepSizes[dimIndex];
}
return linearIndex;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#toLogicalIndex(int)
*/
@Override
public int[] toLogicalIndex(int linearIndex_)
{
int[] logicalIndex = new int[numberOfDimensions()];
for(int dimIndex = 0; dimIndex < logicalIndex.length; dimIndex++)
{
// How many times does the stepSize fit in the index?
logicalIndex[dimIndex] = linearIndex_/_stepSizes[dimIndex];
// Pass the remainder to the next dimension
linearIndex_ -= logicalIndex[dimIndex]*_stepSizes[dimIndex];
}
return logicalIndex;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#iterator()
*/
@Override
public IndexIterator iterator()
{
return new Iterator();
}
@Override
public Iterator rangeIterator(int from_, int to_)
{
return new Iterator(from_, to_);
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#iterator(int)
*/
@Override
public SubSpaceSplitIterator iterator(int... dimensions_)
{
return new SubSpaceSplitIterator(dimensions_);
}
@Override
public SkippingLinearIterator[] parallelIterators(int maxCount_, int... dimensions_)
{
// Ensure minimum one
if(maxCount_ < 1)
{
maxCount_ = 1;
}
final int[] currentIndex = Utils.repeatArray(-1, _sizes.length);
// This iterator is across the correct dimensions
SubSpaceSplitIterator parallelTemplate = new SubSpaceSplitIterator(currentIndex, dimensions_);
SubSpaceSplitIterator preStart = parallelTemplate.getOrthogonalIterator();
// Start the iterator to initialise the index
parallelTemplate.nextInt();
// This one is orthogonal to it
SubIndex orthSubIndex = subIndexAt(parallelTemplate.getCurrentFullIndex());
// want to go back to pre-start
parallelTemplate = preStart.getOrthogonalIterator();
/* Figure out how many elements there are in orthogonal dimensions, and from that the number and of iterators to
* return and the range of each of those iterators
*/
int orthDimsElements = orthSubIndex.numberOfElements();
int orthRange;
int orthCount;
if(orthDimsElements < maxCount_)
{
orthRange = 1;
orthCount = orthDimsElements;
}
else if(orthDimsElements % maxCount_ == 0)
{
orthRange = orthDimsElements/maxCount_;
orthCount = maxCount_;
}
else
{
// maxCount == 1 is caught by the previous condition, so no need to worry
orthRange = orthDimsElements/maxCount_+1;
orthCount = orthDimsElements/orthRange + (orthDimsElements%orthRange == 0 ? 0 : 1);
}
// Reference the step sizes of the template - they are the same for all the parallel iterators
final int[] parallelStepSizes = parallelTemplate._subStepSizes;
// The lenght will again be the same
final int length = parallelTemplate.numberOfElements();
// As will the sizes
final int[] sizes = new int[parallelTemplate._dimensions.length];
// Copy just the sizes of the dimensions being iterated
for(int i = 0; i < sizes.length; i++)
{
sizes[i] = _sizes[parallelTemplate._dimensions[i]];
}
ParallelIterator[] parallelIterators = new ParallelIterator[orthCount];
int from = 0;
for(int i = 0; i < orthCount; i++) {
int to = Math.min(from + orthRange,orthDimsElements);
SkippingLinearIterator orthRangeIter;
if(orthSubIndex._dimensions.length > 0)
{
orthRangeIter = new SubSpaceSplitIterator(preStart.getCurrentFullIndex(), orthSubIndex._dimensions, from, to);
}
else {
to = 1;
orthRangeIter = new SkippingLinearIterator()
{
boolean hasNext = true;
@Override
public boolean hasNext()
{
return hasNext;
}
@Override
public int nextInt()
{
hasNext = false;
return 0;
}
@Override
public void reset()
{
hasNext = true;
}
@Override
public int nextParallel()
{
throw new UnsupportedOperationException();
}
@Override
public boolean hasNextParallel()
{
throw new UnsupportedOperationException();
}
@Override
public int getCurrentFullLinearIndex()
{
return -1;
}
};
}
parallelIterators[i] = new ParallelIterator(parallelStepSizes,
sizes,
length, orthRangeIter, to - from);
from = to;
}
return parallelIterators;
}
public class Iterator implements IndexIterator
{
private final int[] _currentIndex;
private int _linearIndex = 0;
private final int _last;
private int _from;
public Iterator()
{
_currentIndex = new int[_sizes.length];
// Initialise the highest index to -1, i.e. pre-first element
_currentIndex[getCurrentIndex().length - 1] = -1;
_last = numberOfElements();
reset();
}
public Iterator(int from_, int to_)
{
_from = from_;
_linearIndex = from_;
if(from_ > 0)
{
// Initialise the highest index to -1, i.e. pre-first element
_currentIndex = toLogicalIndex(from_);
}
else
{
_currentIndex = new int[_sizes.length];
}
// Initialise the highest index to -1, i.e. pre-first element
_currentIndex[_currentIndex.length-1] -= 1;
if(to_ > numberOfElements())
{
to_ = numberOfElements();
}
if(from_ > to_)
{
throw new NumericsException("The start of the iterator must be before the end");
}
_last = to_;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIterator#hasNext()
*/
@Override
public boolean hasNext()
{
// Is the linear index smaller than the maximal one?
return _linearIndex < _last;
}
@Override
public Integer next()
{
throw new UnsupportedOperationException("Use nextInt");
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIterator#next()
*/
@Override
public int nextInt()
{
if(_linearIndex == _last)
{
throw new NoSuchElementException();
}
// Start from the highest dimension
int dimIndex = _sizes.length-1;
do
{
// Increment the index in the current dimension
_currentIndex[dimIndex]++;
// If we didn't overflow, we're done!
if(_currentIndex[dimIndex] != _sizes[dimIndex])
{
break;
}
// Otherwise, set the current dimension to 0 and continue
_currentIndex[dimIndex] = 0;
}while (dimIndex-- > 0 );
// increment the linear index and return it
return _linearIndex++;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIterator#remove()
*/
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIterator#getCurrentIndex()
*/
@Override
public int[] getCurrentIndex()
{
return _currentIndex;
}
@Override
public void forEachRemaining(IntConsumer action_)
{
// TODO Auto-generated method stub
}
@Override
public void reset()
{
Arrays.fill(_currentIndex, 0);
// Initialise the highest index to -1, i.e. pre-first element
_currentIndex[getCurrentIndex().length - 1] = -1;
_linearIndex = _from;
}
}
public class ParallelIterator implements IndexIterator, SkippingLinearIterator
{
private final int[] _currentIndex;
private int _linearIndex;
private int _fullLinearIndex;
private final int[] _subStepSizes;
private final int[] _sizes;
private int _last;
private SkippingLinearIterator _orthRangeIter;
private final int _numParallel;
public ParallelIterator(int[] subStepSizes_, int[] sizes_, int length_,
SkippingLinearIterator orthRangeIter_, int numParallel_)
{
_numParallel = numParallel_;
_currentIndex = new int[subStepSizes_.length];
_subStepSizes = subStepSizes_;
_sizes = sizes_;
_last = length_;
_orthRangeIter = orthRangeIter_;
_fullLinearIndex = _orthRangeIter.getCurrentFullLinearIndex();
}
@Override
public boolean hasNext()
{
// Is the linear index smaller than the maximal one?
return _linearIndex < _last;
}
@Override
public Integer next()
{
throw new UnsupportedOperationException("Use nextInt");
}
@Override
public int nextInt()
{
// Start from the highest dimension
int dimIndex = _currentIndex.length-1;
// Does this iterator have any dimensions
if(dimIndex >= 0)
{
do
{
// Increment the index in the current dimension
_currentIndex[dimIndex]++;
// If we didn't overflow, we're done!
if(_currentIndex[dimIndex] != _sizes[dimIndex])
{
_fullLinearIndex += _subStepSizes[dimIndex];
break;
}
// If we overflowed the lowest dimension we're beyond the edge of space
if(dimIndex == 0)
{
throw new NoSuchElementException();
}
// Otherwise, set the current dimension to 0 and continue
_currentIndex[dimIndex] = 0;
}while (dimIndex-- > 0 );
}
// increment the linear index and return it
return _linearIndex++;
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
@Override
public int[] getCurrentIndex()
{
return _currentIndex;
}
public int getCurrentFullLinearIndex()
{
return _fullLinearIndex;
}
@Override
public void forEachRemaining(IntConsumer action_)
{
// TODO Auto-generated method stub
}
@Override
public boolean hasNextParallel()
{
return _orthRangeIter.hasNext();
}
@Override
public int nextParallel()
{
int result = _orthRangeIter.nextInt();
_fullLinearIndex = _orthRangeIter.getCurrentFullLinearIndex();
Arrays.fill(_currentIndex, 0);
_currentIndex[_currentIndex.length - 1] = -1;
_linearIndex = 0;
return result;
}
@Override
public void reset()
{
throw new UnsupportedOperationException("Parallel iterators are not resettable");
}
public int getNumParallel()
{
return _numParallel;
}
}
public class SubSpaceSplitIterator implements SubIndexIterator, SkippingLinearIterator
{
private final int[] _currentIndex;
private final int[] _dimensions;
private int[] _dimensionsToIgnore;
private int _linearIndex = 0;
private int _fullLinearIndex = 0;
private final int[] _subStepSizes;
private int _last;
private final boolean _orthStarted;
private int _from = -1;
private int _to = -1;
public SubSpaceSplitIterator(int[] dimensions_)
{
this(Utils.repeatArray(-1, _sizes.length),dimensions_);
}
public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_)
{
this(currentIndex_, dimensions_, new int[0]);
}
protected SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int [] dimensionsToIgnore_)
{
_dimensions = dimensions_;
_currentIndex = Arrays.copyOf(currentIndex_,currentIndex_.length);
_dimensionsToIgnore = dimensionsToIgnore_;
// The orthogonal iterator is already at some place if the dimensions to ignore are well-defined
boolean orthStarted = true;
for (int i = 0; i < currentIndex_.length; i++) {
if(!ArrayUtils.contains(dimensions_, i) && currentIndex_[i] == -1)
{
orthStarted = false;
break;
}
}
_orthStarted = orthStarted;
int subNumberOfElements = 1;
int[] subStepSizes = new int[_dimensions.length];
int[] stepSizeHelper = new int[_dimensions.length];
// Set the dimensions this iterator iterates over to 0 in the index
for(int dimIndex = _dimensions.length - 1; dimIndex >= 0; dimIndex--)
{
// Initialise the index elements to 0, except the highest - see below
_currentIndex[_dimensions[dimIndex]] = 0;
subNumberOfElements *= _sizes[_dimensions[dimIndex]];
if(dimIndex == _dimensions.length - 1)
{
// For the highest dimension iterated over it is just the stepsize
subStepSizes[dimIndex] = _stepSizes[_dimensions[dimIndex]];
}
else
{
// This may seem like a complicated way to do it but the possibility of
// transposed dimensions means we can't do it more succinctly
stepSizeHelper[dimIndex] = _sizes[_dimensions[dimIndex+1]]*(stepSizeHelper[dimIndex+1] + subStepSizes[dimIndex+1])- subStepSizes[dimIndex+1];
subStepSizes[dimIndex] = _stepSizes[_dimensions[dimIndex]] - stepSizeHelper[dimIndex];
}
}
_subStepSizes = subStepSizes;
_last = subNumberOfElements;
_fullLinearIndex = toLinearIndex(_currentIndex);
if(numberOfDimensions() > 0)
{
_fullLinearIndex -= _subStepSizes[_dimensions.length -1];
// Except the highest, which we set to -1 to indicate 'pre-start'
_currentIndex[_dimensions[_dimensions.length - 1]] = -1;
}
}
public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int[] dimensionsToIgnore_, int from_, int to_)
{
this(currentIndex_, dimensions_, dimensionsToIgnore_);
_from = from_;
_to = to_;
if(_to < _last)
{
_last = _to;
}
initCurrentIndex();
}
public SubSpaceSplitIterator(int[] currentIndex_, int[] dimensions_, int from_, int to_)
{
this(currentIndex_, dimensions_, new int[0]);
_from = from_;
_to = to_;
if(_to < _last)
{
_last = _to;
}
initCurrentIndex();
}
/**
* @throws IndexOutOfBoundsException
*/
private void initCurrentIndex() throws IndexOutOfBoundsException
{
int from = _from;
if (from == -1)
{
from = 0;
}
_linearIndex = from;
if(_dimensions.length == 0) {
return;
}
int[] stepSizes = new int[_dimensions.length];
stepSizes[_dimensions.length-1] = 1;
for(int dim = _dimensions.length-2; dim >= 0; dim --)
{
stepSizes[dim] = stepSizes[dim+1]*_sizes[_dimensions[dim+1]];
}
for(int dim = 0; dim < _subStepSizes.length - 1; dim++)
{
if(from < stepSizes[dim])
{
_currentIndex[_dimensions[dim]] = 0;
continue;
}
else
{
_currentIndex[_dimensions[dim]] = from/stepSizes[dim];
from = from % stepSizes[dim];
}
}
// The remainder goes in the last dimension
_currentIndex[_dimensions[_dimensions.length-1]] = from;
_fullLinearIndex = toLinearIndex(_currentIndex);
if(numberOfDimensions() > 0)
{
_fullLinearIndex -= _subStepSizes[_dimensions.length -1];
// Except the highest, which we set to -1 to indicate 'pre-start'
_currentIndex[_dimensions[_dimensions.length - 1]] -= 1;
}
}
public int numberOfDimensions()
{
return _dimensions.length;
}
public int numberOfElements()
{
return _last;
}
@Override
public boolean hasNext()
{
// Is the linear index smaller than the maximal one?
return _linearIndex < _last;
}
@Override
public int nextInt()
{
// Start from the highest dimension
int dimIndex = _dimensions.length-1;
// Does this iterator have any dimensions
if(dimIndex >= 0)
{
do
{
// Increment the index in the current dimension
int dimension = _dimensions[dimIndex];
_currentIndex[dimension]++;
// If we didn't overflow, we're done!
if(_currentIndex[dimension] != _sizes[dimension])
{
_fullLinearIndex += _subStepSizes[dimIndex];
break;
}
// If we overflowed the lowest dimension we're beyond the edge of space
if(dimIndex == 0)
{
throw new NoSuchElementException();
}
// Otherwise, set the current dimension to 0 and continue
_currentIndex[dimension] = 0;
}while (dimIndex-- > 0 );
}
// increment the linear index and return it
return _linearIndex++;
}
@Override
public Integer next()
{
throw new UnsupportedOperationException("use nextInt");
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* @return the currentIndex
*/
@Override
public int[] getCurrentIndex()
{
if(numberOfDimensions() == 0)
{
// The linear index is post-incremented in next, so we need to use the value one earlier
return new int[]{_linearIndex-1};
}
int[] subIndex = new int[_dimensions.length];
for (int i = 0; i < subIndex.length; i++)
{
subIndex[i] = _currentIndex[_dimensions[i]];
}
return subIndex;
}
public SubSpaceSplitIterator getOrthogonalIterator()
{
// Clone the index so that we can work in a multi-threaded environment
int[] clonedIndex = Arrays.copyOf(_currentIndex,_currentIndex.length);
if(numberOfDimensions() + _dimensionsToIgnore.length == Index.this.numberOfDimensions())
{
return new SubSpaceSplitIterator(clonedIndex,new int[]{},_dimensionsToIgnore);
}
int[] otherDimensions = new int[_sizes.length - _dimensions.length-_dimensionsToIgnore.length];
int otherDimIndex = 0;
for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
{
if(!ArrayUtils.contains(_dimensions, dimIndex) && !ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
{
otherDimensions[otherDimIndex++] = dimIndex;
}
}
return new SubSpaceSplitIterator(clonedIndex, otherDimensions,_dimensionsToIgnore);
}
@Override
public int[] getOtherIndex()
{
int[] otherIndex = new int[_sizes.length - _dimensions.length-_dimensionsToIgnore.length];
int otherDimIndex = 0;
for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
{
if(!ArrayUtils.contains(_dimensions, dimIndex) && !ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
{
otherIndex[otherDimIndex++] = _currentIndex[dimIndex];
}
}
return otherIndex;
}
public boolean orthStarted()
{
return _orthStarted;
}
@Override
public int[] getCurrentFullIndex()
{
int[] fullIndex = new int[_sizes.length - _dimensionsToIgnore.length];
int otherDimIndex = 0;
for(int dimIndex = 0; dimIndex < _sizes.length; dimIndex++)
{
if(!ArrayUtils.contains(_dimensionsToIgnore, dimIndex))
{
fullIndex[otherDimIndex++] = _currentIndex[dimIndex];
}
}
return fullIndex;
}
@Override
public int getCurrentFullLinearIndex()
{
return _fullLinearIndex;
}
public int[] getDimensions()
{
return _dimensions;
}
public int[] getSizes()
{
return getSubSizes(_dimensions);
}
public void ignoreDimensions(int[] dimensionsToIgnore_) {
_dimensionsToIgnore = dimensionsToIgnore_;
}
@Override
public void forEachRemaining(IntConsumer action_)
{
// TODO Auto-generated method stub
}
@Override
public boolean hasNextParallel()
{
return false;
}
@Override
public int nextParallel()
{
throw new UnsupportedOperationException();
}
@Override
public void reset()
{
initCurrentIndex();
}
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#iteratorAt(int[])
*/
@Override
public SubIndexIterator iteratorAt(int[] index_)
{
Pair pair = getOtherDimsAndFullIndexAt(index_);
return new SubSpaceSplitIterator(pair.getRight(), pair.getLeft());
}
public Pair getOtherDimsAndFullIndexAt(int[] index_)
{
int[] initialisedIndex = new int[numberOfDimensions()];
// Create arrays that are big enough if needed
int[] otherDimensions = new int[numberOfDimensions()];
int otherCount = 0;
// Determine which dimensions are not tied down in index_; these are the ones to fill along
for(int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
{
if(dimIndex >= index_.length || index_[dimIndex]== -1)
{
otherDimensions[otherCount++] = dimIndex;
initialisedIndex[dimIndex] = -1;
}
else
{
// Copy the index to iterate at to the current index that will be used for the iterator
// We have to do this because index_ may be too short
initialisedIndex[dimIndex] = index_[dimIndex];
}
}
otherDimensions = Arrays.copyOf(otherDimensions,otherCount);
Pair pair = new Pair(otherDimensions, initialisedIndex);
return pair;
}
public Pair getOtherDimsAndFullIndexAt(int[] index_, int[] dimensionsToIgnore_)
{
int[] initialisedIndex = new int[numberOfDimensions()-dimensionsToIgnore_.length];
// Create arrays that are big enough if needed
int[] otherDimensions = new int[numberOfDimensions()-dimensionsToIgnore_.length];
int otherCount = 0;
int subDimIndex = 0;
int dimToIgnoreIndex = 0;
// Determine which dimensions are not tied down in index_; these are the ones to fill along
for(int dimIndex = 0; dimIndex < numberOfDimensions(); dimIndex++)
{
// Skip the dimension if it is to be ignored
if(dimIndex == dimensionsToIgnore_[dimToIgnoreIndex])
{
dimToIgnoreIndex++;
continue;
}
if(dimIndex >= index_.length || index_[dimIndex]== -1)
{
otherDimensions[otherCount++] = dimIndex;
initialisedIndex[subDimIndex] = -1;
}
else
{
// Copy the index to iterate at to the current index that will be used for the iterator
// We have to do this because index_ may be too short
initialisedIndex[subDimIndex] = index_[dimIndex];
}
subDimIndex++;
}
otherDimensions = Arrays.copyOf(otherDimensions,otherCount);
Pair pair = new Pair(otherDimensions, initialisedIndex);
return pair;
}
/* (non-Javadoc)
* @see com.meliorbis.numerics.IMultiDimensionalIndex#subIndexAt(int[])
*/
@Override
public SubIndex subIndexAt(int[] atPosition_)
{
return new SubIndex(atPosition_);
}
public int[] getSubSizes(int[] dimensions)
{
if(dimensions.length == 0)
{
return new int[]{1};
}
int[] sizes = new int[dimensions.length];
for (int i = 0; i < sizes.length; i++)
{
// Get the size of each dimension that is part of this iterator
sizes[i] = _sizes[dimensions[i]];
}
return sizes;
}
public class SubIndex implements com.meliorbis.numerics.index.SubIndex
{
private final int[] _currentIndex;
private final int[] _dimensions;
private final int[] _subSizes;
private final int _subNumberOfElements;
private final Index _subIndex;
private SubIndex(int[] indexAt_)
{
if(FORCE_CORRECT_DIMS && Index.this._sizes.length != indexAt_.length)
{
throw new RuntimeException("Incorrect number of dims");
}
Pair otherDimsAndFullIndexAt = getOtherDimsAndFullIndexAt(indexAt_);
_dimensions = otherDimsAndFullIndexAt.getLeft();
_subSizes = getSubSizes(otherDimsAndFullIndexAt.getLeft());
_currentIndex = otherDimsAndFullIndexAt.getRight();
_subIndex = new Index(_subSizes);
int subNumberOfElements = 1;
for (int i = 0; i < _subSizes.length; i++)
{
subNumberOfElements *= _subSizes[i];
}
_subNumberOfElements = subNumberOfElements;
}
private SubIndex(int[] indexAt_, int[] dimensionOrder_)
{
Pair otherDimsAndFullIndexAt = getOtherDimsAndFullIndexAt(indexAt_);
_dimensions = dimensionOrder_;
_subSizes = getSubSizes(dimensionOrder_);
_currentIndex = otherDimsAndFullIndexAt.getRight();
_subIndex = new Index(_subSizes);
int subNumberOfElements = 1;
for (int i = 0; i < _subSizes.length; i++)
{
subNumberOfElements *= _subSizes[i];
}
_subNumberOfElements = subNumberOfElements;
}
@Override
public int[] getSizes()
{
return _subSizes;
}
@Override
public int numberOfElements()
{
return _subNumberOfElements;
}
@Override
public int numberOfDimensions()
{
return getDimensions().length;
}
@Override
public int[] fullIndex(int[] subIndex_)
{
int[] currentIndex = Arrays.copyOf(_currentIndex, _currentIndex.length);
if(_dimensions.length == 0)
{
if(subIndex_.length != 1 || subIndex_[0] != 0)
{
throw new RuntimeException("Singleton indices only support index [0]");
}
// All is well, the currentIndex is the daddy
return currentIndex;
}
for (int i = 0; i < subIndex_.length; i++)
{
currentIndex[getDimensions()[i]] = subIndex_[i];
}
return currentIndex;
}
@Override
public int[] subIndex(int[] fullIndex_)
{
int[] subIndex = new int[getDimensions().length];
for (int i = 0; i < subIndex.length; i++)
{
subIndex[i] = fullIndex_[getDimensions()[i]];
}
return subIndex;
}
@Override
public int toLinearIndex(int... logicalIndex_)
throws IndexOutOfBoundsException
{
return _subIndex.toLinearIndex(logicalIndex_);
}
@Override
public int[] toLogicalIndex(int linearIndex_)
{
return _subIndex.toLogicalIndex(linearIndex_);
}
@Override
public IndexIterator iterator()
{
return Index.this.new SubSpaceSplitIterator(_currentIndex, getDimensions());
}
@Override
public SubSpaceSplitIterator rangeIterator(int from_, int to_)
{
return Index.this.new SubSpaceSplitIterator(_currentIndex, getDimensions(), from_, to_);
}
@Override
public SubSpaceSplitIterator iterator(int... dimensions_)
{
int[] dimensionsToIgnore = otherDimensions();
return Index.this.new SubSpaceSplitIterator(_currentIndex.clone(),subDimensionsInFullIndex(dimensions_),dimensionsToIgnore);
}
@Override
public SkippingLinearIterator[] parallelIterators(int maxCount_, int... dimensions_)
{
// Ensure minimum one
if(maxCount_ < 1)
{
maxCount_ = 1;
}
int[] dimensionsToIgnore = otherDimensions();
// This iterator is across the correct dimensions
SubSpaceSplitIterator parallelTemplate = new SubSpaceSplitIterator(_currentIndex.clone(),
subDimensionsInFullIndex(dimensions_),
dimensionsToIgnore);
SubSpaceSplitIterator preStart = parallelTemplate.getOrthogonalIterator();
// Start the iterator to initialise the index
parallelTemplate.nextInt();
// This one is orthogonal to it
SubIndex orthSubIndex = subIndexAt(parallelTemplate.getCurrentFullIndex());
// want to go back to pre-start
parallelTemplate = preStart.getOrthogonalIterator();
/* Figure out how many elements there are in orthogonal dimensions, and from that the number and of iterators to
* return and the range of each of those iterators
*/
int orthDimsElements = orthSubIndex.numberOfElements();
int orthRange;
int orthCount;
if(orthDimsElements < maxCount_)
{
orthRange = 1;
orthCount = orthDimsElements;
}
else
{
orthRange = orthDimsElements/maxCount_ + (orthDimsElements % maxCount_ == 0 ? 0 : 1);
orthCount = maxCount_;
}
// Reference the step sizes of the template - they are the same for all the parallel iterators
final int[] parallelStepSizes = parallelTemplate._subStepSizes;
// The length will again be the same
final int length = parallelTemplate.numberOfElements();
// As will the sizes
final int[] sizes = new int[parallelTemplate._dimensions.length];
// Copy just the sizes of the dimensions being iterated
for(int i = 0; i < sizes.length; i++)
{
sizes[i] = _sizes[parallelTemplate._dimensions[i]];
}
ParallelIterator[] parallelIterators = new ParallelIterator[orthCount];
int from = 0;
for(int i = 0; i < orthCount; i++) {
int to = Math.min(from + orthRange,orthDimsElements);
SubSpaceSplitIterator orthRangeIter = new SubSpaceSplitIterator(
preStart._currentIndex.clone(), orthSubIndex._dimensions,
from, to);
parallelIterators[i] = new ParallelIterator(parallelStepSizes,
sizes,
length, orthRangeIter, to - from);
from = to;
}
return parallelIterators;
}
/**
* @return Returns the dimensions to ignore in the full space when a subspaceSplitIteator is created
* on this subindex
*/
@Override
public int[] otherDimensions()
{
int[] dimensionsToIgnore = new int[Index.this.numberOfDimensions() - getDimensions().length];
int dimCount = 0;
// The dimensions that are fixed in this subIndex can be ignored in the split iterator (since it only iterates over the
// subIndex
for(int i = 0; i < Index.this.numberOfDimensions(); i++)
{
if(!ArrayUtils.contains(getDimensions(),i))
{
dimensionsToIgnore[dimCount++] = i;
}
}
return dimensionsToIgnore;
}
/**
* Retrieves the index of the specified sub-space dimensions in the full space
*
* @param dimensions_ The dimensions of this index to get
*
* @return The same dimensions but in the full index
*/
@Override
public int[] subDimensionsInFullIndex(int... dimensions_) {
int[] actualDimensions = new int[dimensions_.length];
for (int i = 0; i < actualDimensions.length; i++)
{
actualDimensions[i] = getDimensions()[dimensions_[i]];
}
return actualDimensions;
}
@Override
public SubIndexIterator iteratorAt(int[] index_)
{
int[] dimensionsToIgnore = otherDimensions();
Pair pair = getOtherDimsAndFullIndexAt(fullIndex(index_));
return Index.this.new SubSpaceSplitIterator(pair.getRight(),pair.getLeft(),dimensionsToIgnore);
}
private int[] subSubIndex(int[] index_)
{
int[] newIndex = Arrays.copyOf(_currentIndex, _currentIndex.length);
for(int i = 0; i < getDimensions().length; i++)
{
// 'unset' the dimensions this index is over, meaning it can't be part of the current index
newIndex[getDimensions()[i]] = -1;
}
// Now set the additional dimensions being fixed
for(int i = 0;i < index_.length; i++)
{
if(index_[i] != -1)
{
newIndex[getDimensions()[i]] = index_[i];
}
}
return newIndex;
}
@Override
public SubIndex subIndexAt(int[] atPosition_)
{
return Index.this.subIndexAt(subSubIndex(atPosition_));
}
@Override
public Index nonSubbed()
{
// Get a multidimensional index with the same sizes as this one
return new Index(getSizes());
}
@Override
public int[] getDimensions() {
return _dimensions;
}
@Override
public com.meliorbis.numerics.index.Index transpose(int dimA_, int dimB_) {
int[] indexAt = new int[_currentIndex.length];
int[] dimensions = Arrays.copyOf(_dimensions, _dimensions.length);
for (int i = 0; i < indexAt.length; i++) {
indexAt[i] = ArrayUtils.contains(dimensions, i)?-1:_currentIndex[i];
}
int tmp = dimensions[dimA_];
dimensions[dimA_] = dimensions[dimB_];
dimensions[dimB_] = tmp;
return new SubIndex(indexAt, dimensions);
}
@Override
public Index reorder(int[] order_)
{
// TODO: Implement - not hard!
throw new UnsupportedOperationException();
}
}
@Override
public Index nonSubbed()
{
// This is a non-subbed index and it is immutable, so return it
return this;
}
@Override
public com.meliorbis.numerics.index.Index transpose(int dimA_, int dimB_) {
if(_sizes.length == 1)
{
// do nothing
return new SubIndex(new int[0], new int[]{0});
}
int[] newDims = new int[_sizes.length];
for (int i = 0; i < newDims.length; i++)
{
newDims[i] = (i == dimA_ ? dimB_: i == dimB_ ? dimA_ : i);
}
return new SubIndex(new int[0], newDims);
}
@Override
public com.meliorbis.numerics.index.Index reorder(int[] order_) {
return new SubIndex(new int[0], order_);
}
@Override
public String toString()
{
return "Index(size=" + Arrays.toString(_sizes) + ")";
}
}