All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.meliorbis.numerics.generic.impl.GenericBlockedArray Maven / Gradle / Ivy

Go to download

A library for working with large multi-dimensional arrays and the functions they represent

There is a newer version: 1.2
Show newest version
package com.meliorbis.numerics.generic.impl;

import java.util.Iterator;

import com.meliorbis.numerics.NumericsException;
import com.meliorbis.numerics.generic.ArrayFactory1D;
import com.meliorbis.numerics.generic.ArrayFactory2D;
import com.meliorbis.numerics.generic.BinaryOp;
import com.meliorbis.numerics.generic.GenericArray;
import com.meliorbis.numerics.generic.Matcher;
import com.meliorbis.numerics.generic.MultiDimensionalArray;
import com.meliorbis.numerics.generic.MultiDimensionalArrayException;
import com.meliorbis.numerics.generic.MultiValuedNaryOp;
import com.meliorbis.numerics.generic.NaryOp;
import com.meliorbis.numerics.generic.ParallelIterator;
import com.meliorbis.numerics.generic.SettableIndexedIterator;
import com.meliorbis.numerics.generic.SettableIterator;
import com.meliorbis.numerics.generic.SubSpaceSplitIterator;
import com.meliorbis.numerics.generic.UnaryOp;
import com.meliorbis.numerics.index.SubIndex;
import com.meliorbis.numerics.index.impl.Index;
import com.meliorbis.numerics.threading.ComputableRecursiveAction;
import com.meliorbis.numerics.threading.Executor;

/**
 * Array implementation for generic types
 */
public abstract class GenericBlockedArray> extends AbstractBlockedArray
                                                                                    implements GenericArray
{
    public GenericBlockedArray(Executor executor_, int[] dimensions_)
    {
        super(executor_, dimensions_);
    }

    public GenericBlockedArray(BlockedArrayData data_, SubIndex dimensionCounter_, Executor executor_)
    {
        super(data_, dimensionCounter_, executor_);
    }

	@Override
    protected BlockedArrayData createData(int[] dimensions_)
    {
        return new GenericBlockedArrayData(dimensions_, getZero(), getArrayFactory1D(), getArrayFactory2D());
    }

    protected GenericBlockedArray(BlockedArrayData data_, Executor executor_)
    {
        super(data_, executor_);
    }

    protected GenericBlockedArray(T[] data_, Executor executor_)
    {
        super(data_, executor_);
    }

    public GenericBlockedArray(BlockedArrayData data_, Index.SubIndex subIndex_, Executor executor_)
    {
        super(data_, subIndex_, executor_);
    }

    @Override
    protected NaryOp getSecondOperandOp()
    {
        return (BinaryOp) (a,b) -> b;
    }

    @Override
    protected BlockedArrayData createDataFromValues(T[] data_)
    {
        final GenericBlockedArrayData data = (GenericBlockedArrayData) createData(new int[]{data_.length, 1});

        for (int i = 0; i < data_.length; i++)
        {
            data.setLinear(data_[i], i);
        }

        return data;
    }

    @Override
    public T get(int... indices_) throws MultiDimensionalArrayException
    {
        return getInternal(indices_);
    }

    @Override
    public T first()
    {
        return get(0);
    }

    @Override
    public T last()
    {
        return get(numberOfElements()-1);
    }

    @Override
    public void set(T val_, int... indices_)
            throws MultiDimensionalArrayException
    {
        setInternal(val_, indices_);
    }


    protected T getInternal(int[] indices_)
            throws MultiDimensionalArrayException {
        if (indices_.length == 1) {
            if (_subIndex != null) {
                indices_ = _subIndex.toLogicalIndex(indices_[0]);
            } else {
                return ((GenericBlockedArrayData)_data).getLinear(indices_[0]);
            }
        }
        if (_subIndex != null) {
            indices_ = ((Index.SubIndex) _subIndex).fullIndex(indices_);
        }
        return ((GenericBlockedArrayData)_data).get(indices_);
    }

    protected void setInternal(T val_, int[] indices_)
            throws MultiDimensionalArrayException {
        if (indices_.length == 1) {
            if (_subIndex != null) {
                indices_ = _subIndex.toLogicalIndex(indices_[0]);
            } else {
                ((GenericBlockedArrayData)_data).setLinear(val_, indices_[0]);
                return;
            }
        }
        if (_subIndex != null) {
            indices_ = ((Index.SubIndex) _subIndex).fullIndex(indices_);
        }
        ((GenericBlockedArrayData)_data).set(val_, indices_);
    }

    @Override
    public void setMatching(final T matching_, final T val_)
            throws MultiDimensionalArrayException
    {
        setMatching(new Matcher()
        {
            @Override
            public boolean matches(T otherVal_)
            {
                return matching_.equals(otherVal_);
            }
        }, val_);
    }

    @Override
    public void setMatching(Matcher matcher_, T val_)
    {
    	modifying().map((UnaryOp) v -> matcher_.matches(v) ? val_ : v);
    }

    /* (non-Javadoc)
	 * @see com.meliorbis.numerics.generic.IMultiDimensionalArray#fill(T[])
	 */
    @SuppressWarnings("unchecked")
	@Override
    public R fill(final T... data_) throws MultiDimensionalArrayException {

        // Check that the data is the right size
        if(numberOfElements() % data_.length != 0)
        {
            throw new MultiDimensionalArrayException("The array size must be a multiple of input data length");
        }
        
        SettableIndexedIterator targetIterator = iterator();

        int i = 0;
        
        while(targetIterator.hasNext())
        {
        	// Reset to go around the array again
            if(i == data_.length)
            {
            	i = 0;
            }
            
            targetIterator.next();
            
            targetIterator.set(data_[i++]);
        }
        
        return (R) this;
    }

    @Override
    public void fillDimensions(T[] values_, int... dimensions_)
            throws MultiDimensionalArrayException
    {
    	R filler = createNew(values_.length).fill(values_);
    	fillDimensions(filler, dimensions_);
    }

    @Override
    public T sum() throws MultiDimensionalArrayException
    {
        return reduce(new Reduction()
		{

			@Override
			public T perform(SettableIterator iterator_) throws RuntimeException
			{
				 // Initialise the sum to zero
	            T sum = getZero();

	            // Successively add each value of the iterator to the sum
	            while(iterator_.hasNext())
	            {
	                sum = getAddOp().perform(sum, iterator_.next());
	            }

	            // Return the calculated sum
	            return sum;
			}
		});
    }

    @Override
    public T mean(MultiDimensionalArray levels_, int dimension_) throws MultiDimensionalArrayException
    {
        SubSpaceSplitIterator levelsDimIter = iteratorAcross(new int[]{dimension_});

        T mean = getZero();

        final SettableIndexedIterator levelIter = levels_.iterator();

        while (levelsDimIter.hasNext())
        {
            // Go to the next level
            levelsDimIter.next();

            // Get the level at that index from the passed levels
            T level = levelIter.next();

            // iterate over all points at that level, in other dimensions
            SubSpaceSplitIterator pointsWithLevelIter = levelsDimIter.getOrthogonalIterator();

            // Calculate the total weight at the current level
            T weightAtLevel = getZero();

            while (pointsWithLevelIter.hasNext())
            {
                T weight = (T) pointsWithLevelIter.next();

                weightAtLevel = getAddOp().perform(weightAtLevel, weight);
            }

            // add the weight times the level to the mean
            mean = getAddOp().perform(mean, getMultOp().perform(level, weightAtLevel));
        }

        return mean;
    }

    @Override
    public T secondMoment(MultiDimensionalArray levels_, int dimension_)
    {
        SubSpaceSplitIterator levelsDimIter = iteratorAcross(new int[]{dimension_});

        T sumOfSquares = getZero();

        BinaryOp mult = getMultOp();
        BinaryOp add = getAddOp();

        final SettableIndexedIterator levelIter = levels_.iterator();

        while (levelsDimIter.hasNext())
        {
            // Go to the next level
            levelsDimIter.next();

            // Get the level at that index from the passed levels
            T level = levelIter.next();

            // iterate over all points at that level, in other dimensions
            SubSpaceSplitIterator pointsWithLevelIter = levelsDimIter.getOrthogonalIterator();

            // Calculate the total weight at the current level
            T weightAtLevel = getZero();

            while (pointsWithLevelIter.hasNext())
            {
                T weight = (T) pointsWithLevelIter.next();

                weightAtLevel = add.perform(weightAtLevel, weight);
            }

            // add the weight times the level squared to the sum of squares
            sumOfSquares = add.perform(sumOfSquares,mult.perform(mult.perform(level,level),weightAtLevel));
        }

        // mean of squares minus square of means = variance
        return sumOfSquares;
    }

   


    @Override
    public  R multiply(MultiDimensionalArray other_)
    {
        return with(other_).map(getMultOp());
    }

    @Override
    public  R add(MultiDimensionalArray other_)
    {
        return with(other_).map(getAddOp());
    }

    @Override
    public  R divide(MultiDimensionalArray other_)
    {
        return with(other_).map(getDivisionOp());
    }
    
  
    
    @Override
    public  R subtract(MultiDimensionalArray other_)
    {
        return with(other_).map(getSubtractionOp());
    }

    public UnaryOp getDivisionOp(T denominator_)
    {
        return binaryOpWithConstant(getDivisionOp(), denominator_);
    }
    
    public UnaryOp getMultOp(T multiplicand_)
    {
        return binaryOpWithConstant(getMultOp(), multiplicand_);
    }

    public UnaryOp getAddOp(T summand_)
    {
        return binaryOpWithConstant(getAddOp(), summand_);
    }
    
    public UnaryOp getSubtractionOp(T summand_)
    {
        return binaryOpWithConstant(getSubtractionOp(), summand_);
    }

    protected UnaryOp binaryOpWithConstant(BinaryOp op_, T constant_)
    {
        return (x) -> op_.perform(x, constant_);
    }

    @Override
    public final R add(T summand_)
    {
        return map(getAddOp(summand_));
    }
    
    @Override
    public R multiply(T multiplicand_)
    {
    	return map(getMultOp(multiplicand_));
    }
    
    @Override
    public R divide(T other_)
    {
        return map(getDivisionOp(other_));
    }
    
    @Override
    public R subtract(T other_)
    {
        return map(getSubtractionOp(other_));
    }

    @Override
    public T[] toArray()
    {
        // If the array is not restricted, let the data object do the work.
        if(_subIndex == null)
        {
            return ((GenericBlockedArrayData)_data).toArray();
        }
        else
        {
            // Otherwise, iterate over the array and copy each element.
            T[] data = getArrayFactory1D().createArray(numberOfElements());

            SettableIndexedIterator iterator = iterator();

            int index = 0;
            while(iterator.hasNext())
            {
                data[index++] = iterator.next();
            }

            return data;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object obj_)
    {
        if(!(obj_ instanceof MultiDimensionalArray))
        {
            return false;
        }

        final boolean same[] = new boolean[]{true};

        try {
	        with((MultiDimensionalArray)obj_).map((BinaryOp) (left_, right_) -> {
	            if (left_ == null)
	            {
	                if (right_ != null)
	                {
	                    same[0] = false;
	                }
	            } else if (!left_.equals(right_))
	            {
	                same[0] = false;
	            }
	
	            return getZero();
	        });
        }
        catch(MultiDimensionalArrayException e) {
        	// Silently drop - most likely size difference
        	return false;
        }

        return same[0];
    }

    @Override
    protected  NaryOpCallable createNaryOpCallable(NaryOp op_,
                       SettableIterator[] inputs_, SettableIterator result_, boolean iterateResult_)
    {
        return new GenericNaryOpCallable(op_, inputs_, result_, iterateResult_)
        {
            @Override
            protected T[] createTypedArray(int length_)
            {
                return GenericBlockedArray.this.createTypedArray(length_);
            }
        };
    }
    
    @Override
	protected SettableIterator createMultiSettableIterator(SettableIterator[] iters_)
	{
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected  R[] createPointWiseMultiValuedResult(MultiValuedNaryOp op_,  MultiDimensionalArray[] others_)
	{
		T[] tester = createTypedArray(others_.length + 1);
		
		tester[0] = get(0);
		
		
		for (int j = 0; j < others_.length; j++)
		{
			tester[j+1] = ((GenericBlockedArray)others_[j]).get(0);
		}
		
		try
		{
			T[] results = op_.perform(tester);
			
			R first = createNew(size());
			R[] arrays = (R[]) java.lang.reflect.Array.newInstance(first.getClass(), results.length);
			
			arrays[0] = first;
			
			for (int i = 1; i < arrays.length; i++)
			{
				arrays[i] = createNew(size());
			}
			
			return arrays;

		} catch (Exception ex_)
		{
			throw new NumericsException("Op failed whilst trying to determine output count", ex_);
		}
	}

    @Override
    public R copy()
    {
        return createNew(size()).fill(this);
    }

    @Override
    public T[] createTypedArray(int length_)
    {
        return getArrayFactory1D().createArray(length_);
    }

    protected abstract ArrayFactory2D getArrayFactory2D();

    protected abstract ArrayFactory1D getArrayFactory1D();

	@SuppressWarnings("unchecked")
	@Override
	protected  ComputableRecursiveAction createReduceAction(ReductionBase, E> reduction_,
			SettableIterator currentIterator_, AbstractMappable target_)
	{
		int initialTargetIndex = ((ParallelIterator)currentIterator_).nextParallel();
		
		final SettableIndexedIterator targetIterator = ((GenericBlockedArray)target_).rangeIterator(initialTargetIndex, initialTargetIndex + ((ParallelIterator)currentIterator_).parallelCount());
		return ()-> {
			try
			{
				// Do the first one outside the loop because it was necessary to iterate to the next parallel to get
				// the initial index above
				targetIterator.next();
				targetIterator.set(((Reduction)reduction_).perform(currentIterator_));

				// Do the rest in a loop
				while(((ParallelIterator)currentIterator_).hasNextParallel())
				{
					((ParallelIterator)currentIterator_).nextParallel();
					targetIterator.next();
					targetIterator.set(((Reduction)reduction_).perform(currentIterator_));
				}
			} catch (Exception e)
			{
				throw new MultiDimensionalArrayException(e);
			}
		};
	}
    
    

}