deepboof.Tensor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main Show documentation
Show all versions of main Show documentation
Trainer Agnostic Deep Learning
/*
* Copyright (c) 2016, Peter Abeles. All Rights Reserved.
*
* This file is part of DeepBoof
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package deepboof;
import deepboof.misc.TensorOps;
/**
* Base class for Tensors. A tensor is an N-dimensional array. Array elements are stored in a 1-D array
* in row-major ordering. This class and its direct children provide only a light weight wrapper around the
* array.
*
*
* Sub-tensors are tensors which are wrapped around an external array which it doesn't own. Most of the time,
* sub-tensors will have a non-zero startIndex. They can't be {@link #reshape(int...) reshaped} if the change
* in shape requires the data array to grow.
*
*
* @author Peter Abeles
*/
public abstract class Tensor extends BaseTensor {
/**
* The index in the input array that this tensor starts at. This allows for a tensor to be
* inside a much larger array.
*/
public int startIndex = 0;
/**
* If this tensor is wrapped around another array which it doesn't own then it is a sub-tensor.
* Most of the time, sub-tensors will have a non-zero startIndex. They can't be resized.
*/
public boolean subtensor = false;
/**
* Stride for each axis
*/
public int strides[] = new int[0];
/**
* Accessor function which allows any tensor's element to be read as a double.
* @param coordinate Coordinate of the element which is to be read
* @return Tensor elements's value as a double
*/
public abstract double getDouble( int ...coordinate );
/**
* Returns internal array used to store tensor data. Data is stored in a row-major order in a single array.
* @return tensor data.
*/
public abstract Object getData();
/**
* Used to change the internal array in an abstract way
*/
public abstract void setData( Object data );
/**
* Reshape for an arbitrary number of dimensions
*
* @param shape New shape. Length must be the same as the number of dimensions. Array reference
* not saved internally. Highest to lowest dimension
*/
public void reshape(int... shape) {
if( this.shape.length != shape.length ) {
this.shape = new int[shape.length];
}
System.arraycopy(shape,0,this.shape,0,shape.length);
reshape();
}
/**
* {@link #reshape(int...) Reshape} for 1-D tensors. Convenience function, but can be used
* to avoid declaring a new array
*
* @param length0 Length of axis-0
*/
public void reshape( int length0 ) {
if( shape.length != 1 ) {
shape = new int[1];
}
shape[0] = length0;
reshape();
}
/**
* {@link #reshape(int...) Reshape} for 2-D tensors. Convenience function, but can be used
* to avoid declaring a new array
*
* @param length1 Length of axis-1
* @param length0 Length of axis-0
*/
public void reshape( int length1 , int length0) {
if( shape.length != 2 ) {
shape = new int[2];
}
shape[0] = length1;
shape[1] = length0;
reshape();
}
/**
* {@link #reshape(int...) Reshape} for 3-D tensors. Convenience function, but can be used
* to avoid declaring a new array
*
* @param length2 Length of axis-2
* @param length1 Length of axis-1
* @param length0 Length of axis-0
*/
public void reshape( int length2 , int length1 , int length0 ) {
if( shape.length != 3 ) {
shape = new int[3];
}
shape[0] = length2;
shape[1] = length1;
shape[2] = length0;
reshape();
}
/**
* {@link #reshape(int...) Reshape} for 4-D tensors. Convenience function, but can be used
* to avoid declaring a new array
*
* @param length3 Length of axis-3
* @param length2 Length of axis-2
* @param length1 Length of axis-1
* @param length0 Length of axis-0
*/
public void reshape( int length3 , int length2 , int length1 , int length0 ) {
if( shape.length != 4 ) {
shape = new int[4];
}
shape[0] = length3;
shape[1] = length2;
shape[2] = length1;
shape[3] = length0;
reshape();
}
/**
* {@link #reshape(int...) Reshape} for 5-D tensors. Convenience function, but can be used
* to avoid declaring a new array
*
* @param length4 Length of axis-4
* @param length3 Length of axis-3
* @param length2 Length of axis-2
* @param length1 Length of axis-1
* @param length0 Length of axis-0
*/
public void reshape( int length4 , int length3 , int length2 , int length1 , int length0 ) {
if( shape.length != 5 ) {
shape = new int[5];
}
shape[0] = length4;
shape[1] = length3;
shape[2] = length2;
shape[3] = length1;
shape[4] = length0;
reshape();
}
/**
* Reshape for when the inner shape variable has already been adjusted. Useful for when calls to new
* are being minimized.
*/
public void reshape() {
int N = TensorOps.tensorLength(shape);
computeStrides();
if( innerArrayLength() < N+startIndex ) {
if( subtensor )
throw new IllegalArgumentException("Can't reshape sub-tensors if it requires the data array to grow");
else if( startIndex != 0 )
throw new RuntimeException("BUG: Not a sub-tensor and startIndex isn't zero!");
innerArrayGrow(N);
}
}
/**
* Re-declare inner array so that it is at least of length N
* @param N Desired minimum length of inner array
*/
protected abstract void innerArrayGrow(int N );
/**
* Length of inner array as returned by "data.length"
* @return Length of inner array
*/
protected abstract int innerArrayLength();
/**
* Returns the index of the coordinate. Data array is encoded in a row-major format
*
* @param coordinate Coordinate from highest to lowest axis number/dimension
* @return index of the index in internal data array
*/
public int idx(int ...coordinate ) {
int index = 0;
for (int i = 0; i < coordinate.length; i++) {
index += coordinate[i]*strides[i];
}
return index + startIndex;
}
/**
* Specialized version of {@link #idx(int...)} for 1-D tensors.
*
* @param axis0 axis-0 of coordinate
* @return index in internal data array
*/
public int idx(int axis0 ) {
return startIndex + axis0;
}
/**
* Specialized version of {@link #idx(int...)} for 2-D tensors.
*
* @param axis1 axis-1 of coordinate
* @param axis0 axis-0 of coordinate
* @return index in internal data array
*/
public int idx(int axis1, int axis0 ) {
return startIndex + axis1*strides[0] + axis0;
}
/**
* Specialized version of {@link #idx(int...)} for 3-D tensors.
*
* @param axis2 axis-2 of coordinate
* @param axis1 axis-1 of coordinate
* @param axis0 axis-0 of coordinate
* @return index in internal data array
*/
public int idx(int axis2, int axis1 , int axis0 ) {
return startIndex + axis2*strides[0] + axis1*strides[1] + axis0;
}
/**
* Specialized version of {@link #idx(int...)} for 4-D tensors.
*
* @param axis3 axis-3 of coordinate
* @param axis2 axis-2 of coordinate
* @param axis1 axis-1 of coordinate
* @param axis0 axis-0 of coordinate
* @return index in internal data array
*/
public int idx(int axis3, int axis2 , int axis1 , int axis0 ) {
return startIndex + axis3*strides[0] + axis2*strides[1] + axis1*strides[2] + axis0;
}
/**
* Specialized version of {@link #idx(int...)} for 5-D tensors.
*
* @param axis4 axis-4 of coordinate
* @param axis3 axis-3 of coordinate
* @param axis2 axis-2 of coordinate
* @param axis1 axis-1 of coordinate
* @param axis0 axis-0 of coordinate
* @return index in internal data array
*/
public int idx(int axis4, int axis3 , int axis2 , int axis1 , int axis0 ) {
return startIndex + axis4*strides[0] + axis3*strides[1] + axis2*strides[2] + axis1*strides[3] + axis0;
}
/**
* Computes how many indexes must be steped over to increment a value in each axis
*/
public void computeStrides() {
if( strides.length != shape.length ) {
strides = new int[ shape.length ];
}
int N = 1;
for (int i = shape.length-1; i >= 0; i-- ) {
strides[i] = N;
N *= shape[i];
}
}
/**
* Returns the stride at the specified dimension. If negative it will start counting from the tail
*/
public int stride( int index ) {
if( index < 0 ) {
return strides[strides.length+index];
} else {
return strides[index];
}
}
/**
* Copies the shape of this tensor into the provided int[]
* @param shape Where the shape is to be written to. Must be the correct size
*/
public void copyShape( int shape[] ) {
if( shape.length != this.shape.length )
throw new IllegalArgumentException("Dimension of input shape and actual shape must be the same");
for (int i = 0; i < shape.length; i++) {
shape[i] = this.shape[i];
}
}
/**
* Returns the length of a dimension/axis. If a negative number is passed in it will
* return the distance relative to the end. E.g. -1 = length-1, -2 = length-2
* @param dimension The dimension/axis
* @return length
*/
public int length(int dimension ) {
if( dimension < 0 )
return shape[ shape.length+dimension];
else
return shape[dimension];
}
/**
* Length of used elements in Tensor's data array. Note that the actual data array can be larger
* @return length of used region in data array
*/
public int length() {
if( shape.length == 0 )
return 0;
return shape[0]*strides[0];
}
/**
* Creates a tensor of the same type with the specified shape
* @param shape Shape of the new tensor
* @return New tensor with the specified shape
*/
public abstract T create( int ...shape );
/**
* Creates a tensor of the same type and same shape as this one
* @return New tensor
*/
public T createLike() {
return create(shape);
}
/**
* Creates a new coordinate for this tensor.
* @return coordinate/int array of appropriate length
*/
public int[] createCoor() {
return new int[ getDimension() ];
}
/**
* Converts an array index into a coordinate
* @param index internal array index offset from startIndex
* @param storage (Optional) storage for coordinate. If null a new instance is created
* @return coordinate
*/
public int[] indexToCoor( int index , int []storage ) {
if( storage == null ) {
storage = createCoor();
}
for (int i = 0; i < storage.length; i++) {
storage[i] = index / strides[i];
index -= storage[i]*strides[i];
}
return storage;
}
/**
* Turns 'this' tensor into a copy of the provided tensor.
* @param original Original tensor that's to be copied into this one. Not modified.
*/
public void setTo( T original ) {
reshape(original.getShape());
System.arraycopy(original.getData(), original.startIndex, getData(), startIndex, length());
}
/**
* Sets all elements in the tensor to the value of zero
*/
public abstract void zero();
/**
* Returns a copy of this tensor
*/
public T copy() {
T out = createLike();
out.setTo(this);
return out;
}
/**
* Returns true if it is a sub-tensor.
*
* @return true if sub-tensor or false if not
*/
public boolean isSub() {
return subtensor;
}
/**
* Creates a subtensor from this tensor.
* @param startIndex The start index in this tensor's internal data array
* @param shape Shape of the output tensor
* @return The new sub-tensor.
*
*/
public T subtensor(int startIndex, int[] shape) {
T out = create();
out.setData(getData());
out.startIndex = startIndex;
out.shape = shape;
out.subtensor = true;
out.computeStrides();
return out;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy