com.meliorbis.numerics.generic.impl.AbstractMappable 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.generic.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang.ArrayUtils;
import com.meliorbis.numerics.generic.Acrossable;
import com.meliorbis.numerics.generic.BinaryOp;
import com.meliorbis.numerics.generic.Mappable;
import com.meliorbis.numerics.generic.MappableReducible;
import com.meliorbis.numerics.generic.MappableWithSimpleOps;
import com.meliorbis.numerics.generic.MultiDimensionalArray;
import com.meliorbis.numerics.generic.MultiValuedNaryOp;
import com.meliorbis.numerics.generic.NaryOp;
import com.meliorbis.numerics.generic.ParallelIterator;
import com.meliorbis.numerics.generic.SettableIterator;
import com.meliorbis.numerics.generic.SubSpaceSplitIterator;
import com.meliorbis.numerics.threading.ComputableRecursiveAction;
import com.meliorbis.numerics.threading.ComputableRecursiveTask;
import com.meliorbis.numerics.threading.CurrentThreadExecutor;
import com.meliorbis.numerics.threading.Executor;
import com.meliorbis.utils.Timer;
import com.meliorbis.utils.Timer.Stoppable;
/**
* An abstract implementation of IMappableWithSimpleOps that provides common functionality, and in particular enables
* the modifying() mechanism to work for mapping operations that modify the first operand
*
* @param The member type
* @param The return type of mapping operations
*/
@SuppressWarnings("unchecked")
public abstract class AbstractMappable>
implements Acrossable
{
public static AtomicInteger callCount = new AtomicInteger(0);
public static final int POINTWISE_BATCH_COUNT = 5;
protected final Executor _executor;
public AbstractMappable(Executor executor_)
{
super();
_executor = executor_;
}
@Override
public RM map(final NaryOp op_) throws E
{
// Perform a mapping without any other operands
return with().map(op_);
}
@Override
public RM[] map(final MultiValuedNaryOp op_) throws E
{
// Perform a mapping without any other operands
return with().map(op_);
}
protected boolean isModifying()
{
return false;
}
@Override
public abstract MappableWithSimpleOps> modifying();
@Override
public RM multiply(MultiDimensionalArray other_)
{
return with(other_).map(getMultOp());
}
@Override
public RM divide(MultiDimensionalArray other_)
{
return with(other_).map(getDivisionOp());
}
@Override
public RM add(MultiDimensionalArray other_)
{
return with(other_).map(getAddOp());
}
@Override
public RM subtract(MultiDimensionalArray other_)
{
return with(other_).map(getSubtractionOp());
}
/**
* This method creates a callable task that performs the provided operation on each of the elements of the provided
* of the provided input iterators. Storing the result in the output iterator. All iterators must, therefore, have
* the same number of remaining elements.
*
* @param op_ The operation to perform
* @param inputs_ The inputs to the operation
* @param result_ The iterator in which to store the results
* @param iterateResult_ Indicates whether the result iterator should also be iterated. If it is the same as one of the
* inputs it is not a good idea to iterate it too
* @param The type of exception thrown by the operation
* @return An NaryOpCallable which, when executed, will perform the operation as described
*/
protected abstract NaryOpCallable createNaryOpCallable(
NaryOp op_,
SettableIterator extends T>[] inputs_,
SettableIterator> result_, boolean iterateResult_);
/**
* @return The binary add operation for the underlying type
*/
abstract public BinaryOp getAddOp();
/**
* @return The binary subtract operation for the underlying type
*/
abstract public BinaryOp getSubtractionOp();
/**
* @return The binary multiplication operation for the underlying type
*/
abstract public BinaryOp getMultOp();
/**
* @return The binary multiplication operation for the underlying type
*/
abstract public BinaryOp getDivisionOp();
/**
* @return The result target of pointwise operations
*/
abstract protected RM getPointwiseResult();
@Override
final public MappableReducible across(int... dimensions_)
{
return new AcrossOperand(dimensions_);
}
/**
* This function is actually in IMultiDimensionalArray, which this class does not implement, but it needs to be available
*
* @param dimensions_ The dimensions to iterate over in parallel
*
* @return A list of parallel iterators
*
* @see MultiDimensionalArray#parallelIterators(int[])
*/
abstract public List extends SettableIterator> parallelIterators(int[] dimensions_);
/**
* @param dimensions_ The dimensions to iterate over
*
* @return A split iterator that iterates over the requested dimensions
*
* @see MultiDimensionalArray#iteratorAcross(int[])
*/
abstract public SubSpaceSplitIterator iteratorAcross(int[] dimensions_);
/**
* @return The max reduction for this mappable
*/
abstract public Reduction getMaxOp();
/**
* @return The min reduction for this mappable
*/
abstract public Reduction getMinOp();
/**
* @return The sum reduction for this mappable
*/
abstract public Reduction getSumOp();
/**
* Create an array that has the dimensions not listed from this array
*
* @param dimensions_ The dimensions to ignore
*
* @return An array with the same dimensions as this array except for the dimensions passed, which are dropped
*/
abstract protected MultiDimensionalArray createOtherDimsArray(int[] dimensions_);
/**
* Create an array that has the dimensions not listed from this array, and fill it with the provided values
*
* @param dimensions_ The dimensions to ignore
* @param array_ The values to fill the resulting array with
*
* @return An array with the same dimensions as this array except for the dimensions passed, which are dropped
*/
abstract protected MultiDimensionalArray createOtherDimsArray(int[] dimensions_, T[] array_);
/**
* @param reduction_ The reduction to perform
* @param currentIterator_ The iterator to perform the reduction over
* @param target_ The target_ mappable to fill with the result
*
* @param The type of the exception thrown by the reduction
*
* @return Creates an action that performs the specified reduction
*/
protected abstract ComputableRecursiveAction createReduceAction(
ReductionBase, E> reduction_,
final SettableIterator currentIterator_, AbstractMappable target_);
abstract protected SettableIterator createMultiSettableIterator(SettableIterator[] iters_);
abstract protected RM[] createPointWiseMultiValuedResult(MultiValuedNaryOp op_, MultiDimensionalArray extends T, ?>[] others_);
/**
* Manages 'across' operations that apply other arguments only in certain dimensions, but at each point of the
* dimensions orthogonal to the across dimensions
*/
private class AcrossOperand implements MappableReducible
{
MultiDimensionalArray extends T, ?>[] _others;
final int[] _dimensions;
private AcrossOperand(int[] dimensions_)
{
_dimensions = dimensions_;
// Check that all the across dimensions are in range
for(int dim : dimensions_) {
if( dim >= AbstractMappable.this.numberOfDimensions()) {
throw new ArrayIndexOutOfBoundsException("Dimensions " + dim +" requested, "+AbstractMappable.this.numberOfDimensions() +" dimensions available");
}
}
}
@Override
public MappableWithSimpleOps> modifying()
{
return ((Acrossable)AbstractMappable.this.modifying()).across(_dimensions);
}
@Override
public MappableWithSimpleOps> nonModifying()
{
return ((Acrossable)AbstractMappable.this.nonModifying()).across(_dimensions);
}
@Override
public RM multiply(MultiDimensionalArray other_)
{
_others = new MultiDimensionalArray[]{other_};
return map(getMultOp());
}
@Override
public RM divide(MultiDimensionalArray other_)
{
_others = new MultiDimensionalArray[]{other_};
return map(getDivisionOp());
}
@Override
public RM add(MultiDimensionalArray other_)
{
_others = new MultiDimensionalArray[]{other_};
return map(getAddOp());
}
@Override
public RM subtract(MultiDimensionalArray other_)
{
_others = new MultiDimensionalArray[]{other_};
return map(getSubtractionOp());
}
@Override
public Mappable with(MultiDimensionalArray... otherArrays_)
{
_others = otherArrays_;
/* Wrap the AcrossOperand in an IArrayOperand to prevent access to the ISingleOperand functions
*/
return new Mappable()
{
@Override
public Mappable with(MultiDimensionalArray... otherArrays_)
{
// Add the new arrays to the already added ones
_others = (MultiDimensionalArray extends T, ?>[]) ArrayUtils.addAll(_others, otherArrays_);
// Return self
return this;
}
@Override
public RM map(NaryOp operation_) throws E
{
// Call the outer operand's map operation
return AbstractMappable.AcrossOperand.this.map(operation_);
}
@Override
public RM[] map(MultiValuedNaryOp operation_) throws E
{
// Call the outer operand's map operation
return AbstractMappable.AcrossOperand.this.map(operation_);
}
};
}
@Override
final public RM map(NaryOp operation_) throws E
{
RM result = getPointwiseResult();
boolean iterateResult = !result.isModifying();
// Get parallel iterators over the relevant dimensions
List> sourceIterators = (List>) AbstractMappable.this.parallelIterators(_dimensions);
// Some generics hoops to jump through...
List> targetIterators = iterateResult ? (List>) (List>) ((MultiDimensionalArray)result).parallelIterators(_dimensions) : (List>)(List>) sourceIterators;
mapToResult(operation_, sourceIterators, targetIterators, iterateResult);
return result;
}
@Override
final public RM[] map(MultiValuedNaryOp operation_) throws E
{
RM[] targetArrays = createPointWiseMultiValuedResult(operation_, _others);
List>[] resultIters = new List[targetArrays.length];
for (int i = 0; i < targetArrays.length; i++)
{
resultIters[i] = (List>) targetArrays[i].parallelIterators(_dimensions);
}
List> targetIterators = new ArrayList>();
for(int i = 0; i < resultIters[0].size(); i++)
{
SettableIterator[] iters = new SettableIterator[targetArrays.length];
for(int j = 0; j < resultIters.length; j++)
{
iters[j] = resultIters[j].get(i);
}
targetIterators.add(createMultiSettableIterator(iters));
}
// Get parallel iterators over the relevant dimensions
List> sourceIterators = (List>) AbstractMappable.this.parallelIterators(_dimensions);
mapToResult(operation_, sourceIterators, targetIterators, true);
return (RM[]) targetArrays;
}
protected void mapToResult(NaryOp operation_, List> sourceIterators, List> targetIterators, boolean iterateResult_)
{
List tasks = new ArrayList(sourceIterators.size());
// Iterate over the orthogonal dimension, finding the max at each point
// along the max dimensions
for (int i = 0; i < sourceIterators.size(); i++)
{
final SettableIterator extends T>[] inputs = new SettableIterator[_others.length + 1];
inputs[0] = sourceIterators.get(i);
for (int j = 0; j < _others.length; j++)
{
inputs[j + 1] = _others[j].iterator();
}
final SettableIterator> targetIter = targetIterators.get(i);
final NaryOpCallable op = createNaryOpCallable(operation_, inputs, targetIter, iterateResult_);
tasks.add(()->
{
boolean first = true;
while(((ParallelIterator)inputs[0]).hasNextParallel())
{
((ParallelIterator)inputs[0]).nextParallel();
if(iterateResult_)
{
((ParallelIterator)targetIter).nextParallel();
}
// Reset the other inputs if this is not the first call
if(!first)
{
for (int j = 1; j < inputs.length; j++)
{
inputs[j].reset();
}
}
op.compute();
first = false;
}
});
}
_executor.executeAndWait(tasks);
}
@Override
public RM reduce(ReductionBase, E> reduction_) throws E
{
if(reduction_ instanceof Reduction)
{
final List tasks;
List> sourceIterators = (List>) AbstractMappable.this.parallelIterators(_dimensions);
tasks = new ArrayList(sourceIterators.size());
final RM resultArray = (RM) AbstractMappable.this.createOtherDimsArray(_dimensions);
for (int i = 0; i < sourceIterators.size(); i++)
{
final SettableIterator currentIterator = sourceIterators.get(i);
tasks.add(createReduceAction(reduction_, currentIterator, resultArray));
}
Timer timer = new Timer();
Stoppable stoppable = timer.start("execReduceAcross");
_executor.executeAndWait(tasks);
stoppable.stop();
return resultArray;
}
else
{
ArrayList> tasks = new ArrayList>();
IndexedReduction reduction = (IndexedReduction)reduction_;
SubSpaceSplitIterator dimIter = iteratorAcross(_dimensions);
SubSpaceSplitIterator orthogonalIterator = dimIter.getOrthogonalIterator();
while(orthogonalIterator.hasNext())
{
orthogonalIterator.next();
final SubSpaceSplitIterator currentIterator = orthogonalIterator.getOrthogonalIterator();
tasks.add(()->{
return reduction.perform(currentIterator);
});
}
List results;
Timer timer = new Timer();
Stoppable stoppable = timer.start("execReduceAcrossIndexed");
results = new CurrentThreadExecutor().executeAndGet(tasks);
stoppable.stop();
return (RM) AbstractMappable.this.createOtherDimsArray(_dimensions,(T[])results.toArray());
}
}
@Override
public RM max()
{
return reduce(getMaxOp());
}
@Override
public RM sum()
{
return reduce(getSumOp());
}
@Override
public RM min()
{
return reduce(getMinOp());
}
}
}