KMP library for tensors #300

Merged
grinisrit merged 215 commits from feature/tensor-algebra into dev 2021-05-08 09:48:04 +03:00
2 changed files with 45 additions and 36 deletions
Showing only changes of commit 23ea4a95a1 - Show all commits

View File

@ -37,9 +37,8 @@ public class IntTensor internal constructor(
shape: IntArray, shape: IntArray,
buffer: IntArray, buffer: IntArray,
offset: Int = 0 offset: Int = 0
) : BufferedTensor<Int>(shape, IntBuffer(buffer), offset) ) : BufferedTensor<Int>(shape, IntBuffer(buffer), offset) {
{ internal constructor(bufferedTensor: BufferedTensor<Int>) :
internal constructor(bufferedTensor: BufferedTensor<Int>):
this(bufferedTensor.shape, bufferedTensor.buffer.array(), bufferedTensor.bufferStart) this(bufferedTensor.shape, bufferedTensor.buffer.array(), bufferedTensor.bufferStart)
} }
@ -47,9 +46,8 @@ public class DoubleTensor internal constructor(
shape: IntArray, shape: IntArray,
buffer: DoubleArray, buffer: DoubleArray,
offset: Int = 0 offset: Int = 0
) : BufferedTensor<Double>(shape, DoubleBuffer(buffer), offset) ) : BufferedTensor<Double>(shape, DoubleBuffer(buffer), offset) {
{ internal constructor(bufferedTensor: BufferedTensor<Double>) :
internal constructor(bufferedTensor: BufferedTensor<Double>):
this(bufferedTensor.shape, bufferedTensor.buffer.array(), bufferedTensor.bufferStart) this(bufferedTensor.shape, bufferedTensor.buffer.array(), bufferedTensor.bufferStart)
override fun toString(): String = toPrettyString() override fun toString(): String = toPrettyString()
@ -59,10 +57,17 @@ public class DoubleTensor internal constructor(
internal inline fun BufferedTensor<Int>.asTensor(): IntTensor = IntTensor(this) internal inline fun BufferedTensor<Int>.asTensor(): IntTensor = IntTensor(this)
internal inline fun BufferedTensor<Double>.asTensor(): DoubleTensor = DoubleTensor(this) internal inline fun BufferedTensor<Double>.asTensor(): DoubleTensor = DoubleTensor(this)
internal inline fun <T> TensorStructure<T>.copyToBufferedTensor(): BufferedTensor<T> =
BufferedTensor(
this.shape,
TensorLinearStructure(this.shape).indices().map(this::get).toMutableList().asMutableBuffer(), 0
)
internal inline fun <T> TensorStructure<T>.toBufferedTensor(): BufferedTensor<T> = when (this) { internal inline fun <T> TensorStructure<T>.toBufferedTensor(): BufferedTensor<T> = when (this) {
is BufferedTensor<T> -> this is BufferedTensor<T> -> this
is MutableBufferND<T> -> BufferedTensor(this.shape, this.mutableBuffer, 0) is MutableBufferND<T> -> if (this.strides.strides.toIntArray() contentEquals TensorLinearStructure(this.shape).strides)
else -> BufferedTensor(this.shape, this.elements().map{ it.second }.toMutableList().asMutableBuffer(), 0) BufferedTensor(this.shape, this.mutableBuffer, 0) else this.copyToBufferedTensor()
else -> this.copyToBufferedTensor()
} }
internal val TensorStructure<Double>.tensor: DoubleTensor internal val TensorStructure<Double>.tensor: DoubleTensor
@ -77,3 +82,5 @@ internal val TensorStructure<Int>.tensor: IntTensor
else -> this.toBufferedTensor().asTensor() else -> this.toBufferedTensor().asTensor()
} }
public fun TensorStructure<Double>.toTypedTensor(): DoubleTensor = this.tensor
public fun TensorStructure<Int>.toTypedTensor(): IntTensor = this.tensor

View File

@ -23,62 +23,64 @@ class TestDoubleTensor {
@Test @Test
fun stridesTest() = DoubleTensorAlgebra { fun stridesTest() = DoubleTensorAlgebra {
val tensor = fromArray(intArrayOf(2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) val tensor = fromArray(intArrayOf(2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4))
assertEquals(tensor[intArrayOf(0,1)], 5.8) assertEquals(tensor[intArrayOf(0, 1)], 5.8)
assertTrue(tensor.elements().map{ it.second }.toList().toDoubleArray() contentEquals tensor.buffer.toDoubleArray()) assertTrue(
tensor.elements().map { it.second }.toList().toDoubleArray() contentEquals tensor.buffer.toDoubleArray()
)
} }
@Test @Test
fun getTest() = DoubleTensorAlgebra { fun getTest() = DoubleTensorAlgebra {
val tensor = fromArray(intArrayOf(1,2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) val tensor = fromArray(intArrayOf(1, 2, 2), doubleArrayOf(3.5, 5.8, 58.4, 2.4))
val matrix = tensor[0].as2D() val matrix = tensor[0].as2D()
assertEquals(matrix[0,1], 5.8) assertEquals(matrix[0, 1], 5.8)
val vector = tensor[0][1].as1D() val vector = tensor[0][1].as1D()
assertEquals(vector[0], 58.4) assertEquals(vector[0], 58.4)
matrix[0,1] = 77.89 matrix[0, 1] = 77.89
assertEquals(tensor[intArrayOf(0,0,1)], 77.89) assertEquals(tensor[intArrayOf(0, 0, 1)], 77.89)
vector[0] = 109.56 vector[0] = 109.56
assertEquals(tensor[intArrayOf(0,1,0)], 109.56) assertEquals(tensor[intArrayOf(0, 1, 0)], 109.56)
tensor.matrixSequence().forEach { tensor.matrixSequence().forEach {
val a = it.asTensor() val a = it.asTensor()
val secondRow = a[1].as1D() val secondRow = a[1].as1D()
val secondColumn = a.transpose(0,1)[1].as1D() val secondColumn = a.transpose(0, 1)[1].as1D()
assertEquals(secondColumn[0], 77.89) assertEquals(secondColumn[0], 77.89)
assertEquals(secondRow[1], secondColumn[1]) assertEquals(secondRow[1], secondColumn[1])
} }
} }
@Test @Test
fun bufferProtocol() { fun noBufferProtocol() {
// create buffers // create buffer
val doubleBuffer = DoubleBuffer(doubleArrayOf(1.0,2.0,3.0)) val doubleArray = DoubleBuffer(doubleArrayOf(1.0, 2.0, 3.0))
val doubleList = MutableList(3, doubleBuffer::get)
// create ND buffers // create ND buffers, no data is copied
val ndBuffer = MutableBufferND(DefaultStrides(intArrayOf(3)), doubleBuffer) val ndArray = MutableBufferND(DefaultStrides(intArrayOf(3)), doubleArray)
val ndList = MutableBufferND(DefaultStrides(intArrayOf(3)), doubleList.asMutableBuffer())
// map to tensors // map to tensors
val bufferedTensorBuffer = ndBuffer.toBufferedTensor() // strides are flipped val bufferedTensorArray = ndArray.toBufferedTensor() // strides are flipped so data copied
val tensorBuffer = bufferedTensorBuffer.asTensor() // no data copied val tensorArray = bufferedTensorArray.asTensor() // data not contiguous so copied again
val bufferedTensorList = ndList.toBufferedTensor() // strides are flipped val tensorArrayPublic = ndArray.toTypedTensor() // public API, data copied twice
val tensorList = bufferedTensorList.asTensor() // data copied val sharedTensorArray = tensorArrayPublic.toTypedTensor() // no data copied by matching type
tensorBuffer[intArrayOf(0)] = 55.9 assertTrue(tensorArray.buffer.array() contentEquals sharedTensorArray.buffer.array())
assertEquals(ndBuffer[intArrayOf(0)], 55.9)
assertEquals(doubleBuffer[0], 55.9)
tensorList[intArrayOf(0)] = 55.9 tensorArray[intArrayOf(0)] = 55.9
assertEquals(ndList[intArrayOf(0)], 1.0) assertEquals(tensorArrayPublic[intArrayOf(0)], 1.0)
assertEquals(doubleList[0], 1.0)
tensorArrayPublic[intArrayOf(0)] = 55.9
assertEquals(sharedTensorArray[intArrayOf(0)], 55.9)
assertEquals(bufferedTensorArray[intArrayOf(0)], 1.0)
bufferedTensorArray[intArrayOf(0)] = 55.9
assertEquals(ndArray[intArrayOf(0)], 1.0)
ndList[intArrayOf(0)] = 55.9
assertEquals(doubleList[0], 55.9)
} }
} }