KMP library for tensors #300

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

View File

@ -32,7 +32,7 @@ public interface LinearOpsTensorAlgebra<T, TensorType : TensorStructure<T>> :
public fun TensorType.lu(): Pair<TensorType, IntTensor> public fun TensorType.lu(): Pair<TensorType, IntTensor>
//https://pytorch.org/docs/stable/generated/torch.lu_unpack.html //https://pytorch.org/docs/stable/generated/torch.lu_unpack.html
public fun luPivot(aLU: TensorType, pivots: IntTensor): Triple<TensorType, TensorType, TensorType> public fun luPivot(lu: TensorType, pivots: IntTensor): Triple<TensorType, TensorType, TensorType>
//https://pytorch.org/docs/stable/linalg.html#torch.linalg.svd //https://pytorch.org/docs/stable/linalg.html#torch.linalg.svd
public fun TensorType.svd(): Triple<TensorType, TensorType, TensorType> public fun TensorType.svd(): Triple<TensorType, TensorType, TensorType>

View File

@ -144,5 +144,7 @@ public class RealAnalyticTensorAlgebra:
TODO("Not yet implemented") TODO("Not yet implemented")
} }
}
} public inline fun <R> RealAnalyticTensorAlgebra(block: RealTensorAlgebra.() -> R): R =
RealAnalyticTensorAlgebra().block()

View File

@ -1,6 +1,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.structures.array import space.kscience.kmath.structures.toDoubleArray
import space.kscience.kmath.structures.toIntArray
public class RealLinearOpsTensorAlgebra : public class RealLinearOpsTensorAlgebra :
LinearOpsTensorAlgebra<Double, RealTensor>, LinearOpsTensorAlgebra<Double, RealTensor>,
@ -80,7 +81,7 @@ public class RealLinearOpsTensorAlgebra :
// todo checks // todo checks
val n = lu.shape[0] val n = lu.shape[0]
val p = lu.zeroesLike() val p = lu.zeroesLike()
pivots.buffer.array.forEachIndexed { i, pivot -> pivots.buffer.toIntArray().forEachIndexed { i, pivot ->
p[i, pivot] = 1.0 p[i, pivot] = 1.0
} }
val l = lu.zeroesLike() val l = lu.zeroesLike()
@ -130,4 +131,4 @@ public class RealLinearOpsTensorAlgebra :
} }
public inline fun <R> RealLinearOpsTensorAlgebra(block: RealTensorAlgebra.() -> R): R = public inline fun <R> RealLinearOpsTensorAlgebra(block: RealTensorAlgebra.() -> R): R =
RealTensorAlgebra().block() RealLinearOpsTensorAlgebra().block()

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.structures.array import space.kscience.kmath.structures.toDoubleArray
public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealTensor> { public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealTensor> {
@ -9,7 +9,7 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
check(this.shape contentEquals intArrayOf(1)) { check(this.shape contentEquals intArrayOf(1)) {
"Inconsistent value for tensor of shape ${shape.toList()}" "Inconsistent value for tensor of shape ${shape.toList()}"
} }
return this.buffer.array[0] return this.buffer.toDoubleArray()[0]
} }
override fun zeros(shape: IntArray): RealTensor { override fun zeros(shape: IntArray): RealTensor {
@ -32,13 +32,13 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
override fun RealTensor.copy(): RealTensor { override fun RealTensor.copy(): RealTensor {
// should be rework as soon as copy() method for NDBuffer will be available // should be rework as soon as copy() method for NDBuffer will be available
return RealTensor(this.shape, this.buffer.array.copyOf()) return RealTensor(this.shape, this.buffer.toDoubleArray().copyOf())
} }
override fun Double.plus(other: RealTensor): RealTensor { override fun Double.plus(other: RealTensor): RealTensor {
val resBuffer = DoubleArray(other.buffer.size) { i -> val resBuffer = DoubleArray(other.buffer.size) { i ->
other.buffer.array[i] + this other.buffer.toDoubleArray()[i] + this
} }
return RealTensor(other.shape, resBuffer) return RealTensor(other.shape, resBuffer)
} }
@ -50,34 +50,34 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
val newThis = broadcast[0] val newThis = broadcast[0]
val newOther = broadcast[1] val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.buffer.size) { i -> val resBuffer = DoubleArray(newThis.buffer.size) { i ->
newThis.buffer.array[i] + newOther.buffer.array[i] newThis.buffer.toDoubleArray()[i] + newOther.buffer.toDoubleArray()[i]
} }
return RealTensor(newThis.shape, resBuffer) return RealTensor(newThis.shape, resBuffer)
} }
override fun RealTensor.plusAssign(value: Double) { override fun RealTensor.plusAssign(value: Double) {
for (i in this.buffer.array.indices) { for (i in this.buffer.toDoubleArray().indices) {
this.buffer.array[i] += value this.buffer.toDoubleArray()[i] += value
} }
} }
override fun RealTensor.plusAssign(other: RealTensor) { override fun RealTensor.plusAssign(other: RealTensor) {
//todo should be change with broadcasting //todo should be change with broadcasting
for (i in this.buffer.array.indices) { for (i in this.buffer.toDoubleArray().indices) {
this.buffer.array[i] += other.buffer.array[i] this.buffer.toDoubleArray()[i] += other.buffer.toDoubleArray()[i]
} }
} }
override fun Double.minus(other: RealTensor): RealTensor { override fun Double.minus(other: RealTensor): RealTensor {
val resBuffer = DoubleArray(other.buffer.size) { i -> val resBuffer = DoubleArray(other.buffer.size) { i ->
this - other.buffer.array[i] this - other.buffer.toDoubleArray()[i]
} }
return RealTensor(other.shape, resBuffer) return RealTensor(other.shape, resBuffer)
} }
override fun RealTensor.minus(value: Double): RealTensor { override fun RealTensor.minus(value: Double): RealTensor {
val resBuffer = DoubleArray(this.buffer.size) { i -> val resBuffer = DoubleArray(this.buffer.size) { i ->
this.buffer.array[i] - value this.buffer.toDoubleArray()[i] - value
} }
return RealTensor(this.shape, resBuffer) return RealTensor(this.shape, resBuffer)
} }
@ -87,14 +87,14 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
val newThis = broadcast[0] val newThis = broadcast[0]
val newOther = broadcast[1] val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.buffer.size) { i -> val resBuffer = DoubleArray(newThis.buffer.size) { i ->
newThis.buffer.array[i] - newOther.buffer.array[i] newThis.buffer.toDoubleArray()[i] - newOther.buffer.toDoubleArray()[i]
} }
return RealTensor(newThis.shape, resBuffer) return RealTensor(newThis.shape, resBuffer)
} }
override fun RealTensor.minusAssign(value: Double) { override fun RealTensor.minusAssign(value: Double) {
for (i in this.buffer.array.indices) { for (i in this.buffer.toDoubleArray().indices) {
this.buffer.array[i] -= value this.buffer.toDoubleArray()[i] -= value
} }
} }
@ -105,7 +105,7 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
override fun Double.times(other: RealTensor): RealTensor { override fun Double.times(other: RealTensor): RealTensor {
//todo should be change with broadcasting //todo should be change with broadcasting
val resBuffer = DoubleArray(other.buffer.size) { i -> val resBuffer = DoubleArray(other.buffer.size) { i ->
other.buffer.array[i] * this other.buffer.toDoubleArray()[i] * this
} }
return RealTensor(other.shape, resBuffer) return RealTensor(other.shape, resBuffer)
} }
@ -116,28 +116,28 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
override fun RealTensor.times(other: RealTensor): RealTensor { override fun RealTensor.times(other: RealTensor): RealTensor {
//todo should be change with broadcasting //todo should be change with broadcasting
val resBuffer = DoubleArray(this.buffer.size) { i -> val resBuffer = DoubleArray(this.buffer.size) { i ->
this.buffer.array[i] * other.buffer.array[i] this.buffer.toDoubleArray()[i] * other.buffer.toDoubleArray()[i]
} }
return RealTensor(this.shape, resBuffer) return RealTensor(this.shape, resBuffer)
} }
override fun RealTensor.timesAssign(value: Double) { override fun RealTensor.timesAssign(value: Double) {
//todo should be change with broadcasting //todo should be change with broadcasting
for (i in this.buffer.array.indices) { for (i in this.buffer.toDoubleArray().indices) {
this.buffer.array[i] *= value this.buffer.toDoubleArray()[i] *= value
} }
} }
override fun RealTensor.timesAssign(other: RealTensor) { override fun RealTensor.timesAssign(other: RealTensor) {
//todo should be change with broadcasting //todo should be change with broadcasting
for (i in this.buffer.array.indices) { for (i in this.buffer.toDoubleArray().indices) {
this.buffer.array[i] *= other.buffer.array[i] this.buffer.toDoubleArray()[i] *= other.buffer.toDoubleArray()[i]
} }
} }
override fun RealTensor.unaryMinus(): RealTensor { override fun RealTensor.unaryMinus(): RealTensor {
val resBuffer = DoubleArray(this.buffer.size) { i -> val resBuffer = DoubleArray(this.buffer.size) { i ->
this.buffer.array[i].unaryMinus() this.buffer.toDoubleArray()[i].unaryMinus()
} }
return RealTensor(this.shape, resBuffer) return RealTensor(this.shape, resBuffer)
} }
@ -158,14 +158,14 @@ public open class RealTensorAlgebra : TensorPartialDivisionAlgebra<Double, RealT
newMultiIndex[i] = newMultiIndex[j].also { newMultiIndex[j] = newMultiIndex[i] } newMultiIndex[i] = newMultiIndex[j].also { newMultiIndex[j] = newMultiIndex[i] }
val linearIndex = resTensor.strides.offset(newMultiIndex) val linearIndex = resTensor.strides.offset(newMultiIndex)
resTensor.buffer.array[linearIndex] = this.buffer.array[offset] resTensor.buffer.toDoubleArray()[linearIndex] = this.buffer.toDoubleArray()[offset]
} }
return resTensor return resTensor
} }
override fun RealTensor.view(shape: IntArray): RealTensor { override fun RealTensor.view(shape: IntArray): RealTensor {
return RealTensor(shape, this.buffer.array) return RealTensor(shape, this.buffer.toDoubleArray())
} }
override fun RealTensor.viewAs(other: RealTensor): RealTensor { override fun RealTensor.viewAs(other: RealTensor): RealTensor {

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.structures.array import space.kscience.kmath.structures.toDoubleArray
import kotlin.math.max import kotlin.math.max
@ -55,7 +55,7 @@ internal inline fun broadcastTensors(vararg tensors: RealTensor): List<RealTenso
} }
val curLinearIndex = tensor.strides.offset(curMultiIndex) val curLinearIndex = tensor.strides.offset(curMultiIndex)
resTensor.buffer.array[linearIndex] = tensor.buffer.array[curLinearIndex] resTensor.buffer.toDoubleArray()[linearIndex] = tensor.buffer.toDoubleArray()[curLinearIndex]
} }
res.add(resTensor) res.add(resTensor)
} }

View File

@ -1,7 +1,7 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.structures.array import space.kscience.kmath.structures.toDoubleArray
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -19,6 +19,6 @@ class TestRealTensor {
fun stridesTest(){ fun stridesTest(){
val tensor = RealTensor(intArrayOf(2,2), doubleArrayOf(3.5,5.8,58.4,2.4)) val tensor = RealTensor(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.array) assertTrue(tensor.elements().map{ it.second }.toList().toDoubleArray() contentEquals tensor.buffer.toDoubleArray())
} }
} }

View File

@ -1,6 +1,6 @@
package space.kscience.kmath.tensors package space.kscience.kmath.tensors
import space.kscience.kmath.structures.array import space.kscience.kmath.structures.toDoubleArray
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -10,7 +10,7 @@ class TestRealTensorAlgebra {
fun doublePlus() = RealTensorAlgebra { fun doublePlus() = RealTensorAlgebra {
val tensor = RealTensor(intArrayOf(2), doubleArrayOf(1.0, 2.0)) val tensor = RealTensor(intArrayOf(2), doubleArrayOf(1.0, 2.0))
val res = 10.0 + tensor val res = 10.0 + tensor
assertTrue(res.buffer.array contentEquals doubleArrayOf(11.0,12.0)) assertTrue(res.buffer.toDoubleArray() contentEquals doubleArrayOf(11.0,12.0))
} }
@Test @Test
@ -18,7 +18,7 @@ class TestRealTensorAlgebra {
val tensor = RealTensor(intArrayOf(1), doubleArrayOf(0.0)) val tensor = RealTensor(intArrayOf(1), doubleArrayOf(0.0))
val res = tensor.transpose(0, 0) val res = tensor.transpose(0, 0)
assertTrue(res.buffer.array contentEquals doubleArrayOf(0.0)) assertTrue(res.buffer.toDoubleArray() contentEquals doubleArrayOf(0.0))
assertTrue(res.shape contentEquals intArrayOf(1)) assertTrue(res.shape contentEquals intArrayOf(1))
} }
@ -27,7 +27,7 @@ class TestRealTensorAlgebra {
val tensor = RealTensor(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) val tensor = RealTensor(intArrayOf(3, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
val res = tensor.transpose(1, 0) val res = tensor.transpose(1, 0)
assertTrue(res.buffer.array contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) assertTrue(res.buffer.toDoubleArray() contentEquals doubleArrayOf(1.0, 3.0, 5.0, 2.0, 4.0, 6.0))
assertTrue(res.shape contentEquals intArrayOf(2, 3)) assertTrue(res.shape contentEquals intArrayOf(2, 3))
} }
@ -42,9 +42,9 @@ class TestRealTensorAlgebra {
assertTrue(res02.shape contentEquals intArrayOf(3, 2, 1)) assertTrue(res02.shape contentEquals intArrayOf(3, 2, 1))
assertTrue(res12.shape contentEquals intArrayOf(1, 3, 2)) assertTrue(res12.shape contentEquals intArrayOf(1, 3, 2))
assertTrue(res01.buffer.array contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) assertTrue(res01.buffer.toDoubleArray() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
assertTrue(res02.buffer.array contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) assertTrue(res02.buffer.toDoubleArray() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0))
assertTrue(res12.buffer.array contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0)) assertTrue(res12.buffer.toDoubleArray() contentEquals doubleArrayOf(1.0, 4.0, 2.0, 5.0, 3.0, 6.0))
} }
@Test @Test
@ -70,9 +70,9 @@ class TestRealTensorAlgebra {
assertTrue(res[1].shape contentEquals intArrayOf(1, 2, 3)) assertTrue(res[1].shape contentEquals intArrayOf(1, 2, 3))
assertTrue(res[2].shape contentEquals intArrayOf(1, 2, 3)) assertTrue(res[2].shape contentEquals intArrayOf(1, 2, 3))
assertTrue(res[0].buffer.array contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) assertTrue(res[0].buffer.toDoubleArray() contentEquals doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0))
assertTrue(res[1].buffer.array contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0)) assertTrue(res[1].buffer.toDoubleArray() contentEquals doubleArrayOf(10.0, 20.0, 30.0, 10.0, 20.0, 30.0))
assertTrue(res[2].buffer.array contentEquals doubleArrayOf(500.0, 500.0, 500.0, 500.0, 500.0, 500.0)) assertTrue(res[2].buffer.toDoubleArray() contentEquals doubleArrayOf(500.0, 500.0, 500.0, 500.0, 500.0, 500.0))
} }
@Test @Test
@ -82,14 +82,14 @@ class TestRealTensorAlgebra {
val tensor3 = RealTensor(intArrayOf(1, 1, 1), doubleArrayOf(500.0)) val tensor3 = RealTensor(intArrayOf(1, 1, 1), doubleArrayOf(500.0))
assertTrue((tensor2 - tensor1).shape contentEquals intArrayOf(2, 3)) assertTrue((tensor2 - tensor1).shape contentEquals intArrayOf(2, 3))
assertTrue((tensor2 - tensor1).buffer.array contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0)) assertTrue((tensor2 - tensor1).buffer.toDoubleArray() contentEquals doubleArrayOf(9.0, 18.0, 27.0, 6.0, 15.0, 24.0))
assertTrue((tensor3 - tensor1).shape contentEquals intArrayOf(1, 2, 3)) assertTrue((tensor3 - tensor1).shape contentEquals intArrayOf(1, 2, 3))
assertTrue((tensor3 - tensor1).buffer.array assertTrue((tensor3 - tensor1).buffer.toDoubleArray()
contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0)) contentEquals doubleArrayOf(499.0, 498.0, 497.0, 496.0, 495.0, 494.0))
assertTrue((tensor3 - tensor2).shape contentEquals intArrayOf(1, 1, 3)) assertTrue((tensor3 - tensor2).shape contentEquals intArrayOf(1, 1, 3))
assertTrue((tensor3 - tensor2).buffer.array contentEquals doubleArrayOf(490.0, 480.0, 470.0)) assertTrue((tensor3 - tensor2).buffer.toDoubleArray() contentEquals doubleArrayOf(490.0, 480.0, 470.0))
} }
} }