├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── libraries │ └── KotlinJavaRuntime.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── TensorKotlin.iml ├── src └── jp │ └── co │ └── qoncept │ └── tensorkotlin │ ├── Shape.kt │ ├── Tensor.kt │ ├── TensorMath.kt │ ├── TensorNN.kt │ └── Utils.kt └── test └── jp └── co └── qoncept └── tensorkotlin ├── MatmulPerformanceTest.kt ├── TensorKotlinSample.kt ├── TensorNNTest.kt ├── TensorTest.kt └── TestUtils.kt /.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /.idea/workspace.xml 3 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | TensorKotlin -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/libraries/KotlinJavaRuntime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Qoncept, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TensorKotlin 2 | 3 | _TensorKotlin_ is a lightweight library to calculate tensors, which has similar APIs to [_TensorFlow_](https://www.tensorflow.org/)'s. _TensorKotlin_ is useful to simulate calculating tensors in Kotlin __using models trained by _TensorFlow___. 4 | 5 | ```kotlin 6 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) // [[1, 2, 3], [4, 5, 6]] 7 | val b = Tensor(Shape(2, 3), floatArrayOf(7, 8, 9, 10, 11, 12)) // [[7, 8, 9], 10, 11, 12]] 8 | 9 | val x = a[1, 2] // 6.0f 10 | val sub = a[0..1, 1..2] // [[2, 3], [5, 6]] 11 | 12 | val sum = a + b // [[8, 10, 12], [14, 16, 18]] 13 | val mul = a * b // [[7, 16, 27], [40, 55, 72]] 14 | 15 | val c = Tensor(Shape(3, 1), floatArrayOf(7, 8, 9)) // [[7], [8], [9]] 16 | val matmul = a.matmul(c) // [[50], [122]] 17 | 18 | val zeros = Tensor(Shape(2, 3, 4)) 19 | val ones = Tensor(Shape(2, 3, 4), 1.0f) 20 | ``` 21 | 22 | ## License 23 | 24 | [The MIT License](LICENSE) 25 | -------------------------------------------------------------------------------- /TensorKotlin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/jp/co/qoncept/tensorkotlin/Shape.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import java.util.* 4 | 5 | class Shape(vararg dimensions: Int) { 6 | val dimensions: IntArray = dimensions.clone() 7 | val volume: Int 8 | get() = dimensions.fold(1) { a, x -> a * x } 9 | 10 | override fun equals(other: Any?): Boolean { 11 | if (other !is Shape) { return false } 12 | 13 | return dimensions.size == other.dimensions.size && zipFold(dimensions, other.dimensions, true) { result, a, b -> 14 | if (!result) { return false } 15 | a == b 16 | } 17 | } 18 | 19 | override fun hashCode(): Int { 20 | return dimensions.hashCode() 21 | } 22 | 23 | override fun toString(): String { 24 | return Arrays.toString(dimensions) 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/jp/co/qoncept/tensorkotlin/Tensor.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import java.util.* 4 | 5 | class Tensor(val shape: Shape, val elements: FloatArray) { 6 | constructor(shape: Shape, element: Float = 0.0f) : this(shape, floatArrayOf(shape.volume, element)) { 7 | } 8 | 9 | internal fun index(indices: IntArray): Int { 10 | assert({ indices.size == shape.dimensions.size }, { "`indices.size` must be ${shape.dimensions.size}: ${indices.size}" }) 11 | return shape.dimensions.zip(indices).fold(0) { a, x -> 12 | assert({ 0 <= x.second && x.second < x.first }, { "Illegal index: indices = ${indices}, shape = ${shape}" }) 13 | a * x.first + x.second 14 | } 15 | } 16 | 17 | operator fun get(vararg indices: Int): Float { 18 | return elements[index(indices)] 19 | } 20 | 21 | operator fun get(vararg ranges: IntRange): Tensor { 22 | val size = ranges.size 23 | val shape = ranges.map { x -> x.endInclusive - x.start + 1 } 24 | val reversedShape = shape.reversed() 25 | val indices = IntArray(size) 26 | val elements = FloatArray(shape.fold(1, Int::times)) { 27 | var i = it 28 | var dimensionIndex = size - 1 29 | for (dimension in reversedShape) { 30 | indices[dimensionIndex] = i % dimension + ranges[dimensionIndex].start 31 | i /= dimension 32 | dimensionIndex-- 33 | } 34 | get(*indices) 35 | } 36 | return Tensor(Shape(*shape), elements) 37 | } 38 | 39 | override fun equals(other: Any?): Boolean { 40 | if (other !is Tensor) { return false } 41 | 42 | return shape == other.shape && zipFold(elements, other.elements, true) { result, lhs, rhs -> 43 | if (!result) { return false } 44 | lhs == rhs 45 | } 46 | } 47 | 48 | private inline fun commutativeBinaryOperation(tensor: Tensor, operation: (Float, Float) -> Float): Tensor { 49 | val lSize = shape.dimensions.size 50 | val rSize = tensor.shape.dimensions.size 51 | 52 | if (lSize == rSize) { 53 | assert({ shape == tensor.shape }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 54 | return Tensor(shape, zipMap(elements, tensor.elements, operation)) 55 | } 56 | 57 | val a: Tensor 58 | val b: Tensor 59 | if (lSize < rSize) { 60 | a = tensor 61 | b = this 62 | } else { 63 | a = this 64 | b = tensor 65 | } 66 | assert({ a.shape.dimensions.endsWith(b.shape.dimensions) }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 67 | 68 | return Tensor(a.shape, zipMapRepeat(a.elements, b.elements, operation)) 69 | } 70 | 71 | private inline fun noncommutativeBinaryOperation(tensor: Tensor, operation: (Float, Float) -> Float, reverseOperation: (Float, Float) -> Float): Tensor { 72 | val lSize = shape.dimensions.size 73 | val rSize = tensor.shape.dimensions.size 74 | 75 | if (lSize == rSize) { 76 | assert({ shape == tensor.shape }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 77 | return Tensor(shape, zipMap(elements, tensor.elements, operation)) 78 | } else if (lSize < rSize) { 79 | assert({ tensor.shape.dimensions.endsWith(shape.dimensions) }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 80 | return Tensor(tensor.shape, zipMapRepeat(tensor.elements, elements, reverseOperation)) 81 | } else { 82 | assert({ shape.dimensions.endsWith(tensor.shape.dimensions) }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 83 | return Tensor(shape, zipMapRepeat(elements, tensor.elements, operation)) 84 | } 85 | } 86 | 87 | operator fun plus(tensor: Tensor): Tensor { 88 | return commutativeBinaryOperation(tensor) { lhs, rhs -> lhs + rhs } 89 | } 90 | 91 | operator fun minus(tensor: Tensor): Tensor { 92 | return noncommutativeBinaryOperation(tensor, { lhs, rhs -> lhs - rhs }, { lhs, rhs -> rhs - lhs}) 93 | } 94 | 95 | operator fun times(tensor: Tensor): Tensor { 96 | return commutativeBinaryOperation(tensor) { lhs, rhs -> lhs * rhs } 97 | } 98 | 99 | operator fun div(tensor: Tensor): Tensor { 100 | return noncommutativeBinaryOperation(tensor, { lhs, rhs -> lhs / rhs }, { lhs, rhs -> rhs / lhs}) 101 | } 102 | 103 | operator fun times(scalar: Float): Tensor { 104 | return Tensor(shape, elements.map { it * scalar }) 105 | } 106 | 107 | operator fun div(scalar: Float): Tensor { 108 | return Tensor(shape, elements.map { it / scalar }) 109 | } 110 | 111 | fun matmul(tensor: Tensor): Tensor { 112 | assert({ shape.dimensions.size == 2 }, { "This tensor is not a matrix: shape = ${shape}" }) 113 | assert({ tensor.shape.dimensions.size == 2 }, { "The given tensor is not a matrix: shape = ${tensor.shape}" }) 114 | 115 | val inCols1Rows2 = shape.dimensions[1] 116 | assert({ tensor.shape.dimensions[0] == inCols1Rows2 }, { "Incompatible shapes of matrices: self.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 117 | 118 | val outRows = shape.dimensions[0] 119 | val outCols = tensor.shape.dimensions[1] 120 | 121 | var elements = FloatArray(outRows * outCols) 122 | for (r in 0 until outRows) { 123 | for (i in 0 until inCols1Rows2) { 124 | var elementIndex = r * outCols 125 | val left = this.elements[r * inCols1Rows2 + i] 126 | for (c in 0 until outCols) { 127 | elements[elementIndex] += left * tensor.elements[i * outCols + c] 128 | elementIndex++ 129 | } 130 | } 131 | } 132 | 133 | return Tensor(Shape(outRows, outCols), elements) 134 | } 135 | 136 | override fun toString(): String { 137 | return "Tensor(${shape}, ${Arrays.toString(elements)})" 138 | } 139 | } 140 | 141 | operator fun Float.times(tensor: Tensor): Tensor { 142 | return tensor.times(this) 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/jp/co/qoncept/tensorkotlin/TensorMath.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | fun Tensor.pow(tensor: Tensor):Tensor { 4 | assert({ shape == tensor.shape }, { "Incompatible shapes of tensors: this.shape = ${shape}, tensor.shape = ${tensor.shape}" }) 5 | return Tensor(shape, zipMap(elements, tensor.elements) { a, b -> Math.pow(a.toDouble(), b.toDouble()).toFloat() }) 6 | } 7 | 8 | fun Tensor.pow(scalar: Float): Tensor { 9 | return Tensor(shape,elements.map { Math.pow(it.toDouble(), scalar.toDouble()).toFloat() }) 10 | } 11 | 12 | val Tensor.sin: Tensor 13 | get() = Tensor(shape, elements.map { Math.sin(it.toDouble()).toFloat() }) 14 | 15 | val Tensor.cos: Tensor 16 | get() = Tensor(shape, elements.map { Math.cos(it.toDouble()).toFloat() }) 17 | 18 | val Tensor.tan: Tensor 19 | get() = Tensor(shape, elements.map { Math.tan(it.toDouble()).toFloat() }) 20 | 21 | val Tensor.asin: Tensor 22 | get() = Tensor(shape, elements.map { Math.asin(it.toDouble()).toFloat() }) 23 | 24 | val Tensor.acos: Tensor 25 | get() = Tensor(shape, elements.map { Math.acos(it.toDouble()).toFloat() }) 26 | 27 | val Tensor.atan: Tensor 28 | get() = Tensor(shape, elements.map { Math.atan(it.toDouble()).toFloat() }) 29 | 30 | val Tensor.sinh: Tensor 31 | get() = Tensor(shape, elements.map { Math.sinh(it.toDouble()).toFloat() }) 32 | 33 | val Tensor.cosh: Tensor 34 | get() = Tensor(shape, elements.map { Math.cosh(it.toDouble()).toFloat() }) 35 | 36 | val Tensor.tanh: Tensor 37 | get() = Tensor(shape, elements.map { Math.tanh(it.toDouble()).toFloat() }) 38 | 39 | val Tensor.exp: Tensor 40 | get() = Tensor(shape, elements.map { Math.exp(it.toDouble()).toFloat() }) 41 | 42 | val Tensor.log: Tensor 43 | get() = Tensor(shape, elements.map { Math.log(it.toDouble()).toFloat() }) 44 | 45 | val Tensor.sqrt: Tensor 46 | get() = Tensor(shape, elements.map { Math.sqrt(it.toDouble()).toFloat() }) 47 | 48 | val Tensor.cbrt: Tensor 49 | get() = Tensor(shape, elements.map { Math.cbrt(it.toDouble()).toFloat() }) 50 | 51 | val Tensor.sigmoid: Tensor 52 | get() = Tensor(shape, elements.map { (1.0 / Math.exp(-it.toDouble())).toFloat() }) 53 | -------------------------------------------------------------------------------- /src/jp/co/qoncept/tensorkotlin/TensorNN.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | val Tensor.softmax: Tensor 4 | get() { 5 | val exps = exp 6 | val sum = exps.elements.fold(0.0f) { r, x -> r + x } 7 | return exps / sum 8 | } 9 | 10 | val Tensor.relu: Tensor 11 | get() { 12 | return Tensor(shape, this.elements.map { Math.max(it, 0.0f) }) 13 | } 14 | 15 | fun Tensor.maxPool(kernelSize: IntArray, strides: IntArray): Tensor { 16 | assert({ shape.dimensions.size == 3 }, { "`shape.dimensions.size` must be 3: ${shape.dimensions.size}" }) 17 | assert({ kernelSize.size == 3 }, { "`kernelSize.size` must be 3: ${kernelSize.size}" }) 18 | assert({ kernelSize[2] == 1 } , { "`kernelSize[2]` != 1 is not supported: ${ kernelSize[2] }" }) 19 | assert({ strides.size == 3 }, { "`strides.size` must be 3: ${ strides.size }" }) 20 | assert({ strides[2] == 1 } , { "`strides[2]` != 1 is not supported: ${ strides[2] }" }) 21 | 22 | val inRows = shape.dimensions[0] 23 | val inCols = shape.dimensions[1] 24 | val numChannels = shape.dimensions[2] 25 | 26 | val filterHeight = kernelSize[0] 27 | val filterWidth = kernelSize[1] 28 | 29 | val inMinDy = -(filterHeight - 1) / 2 30 | val inMaxDy = inMinDy + filterHeight - 1 31 | val inMinDx = -(filterWidth - 1) / 2 32 | val inMaxDx = inMinDx + filterWidth - 1 33 | 34 | val rowStride = strides[0] 35 | val colStride = strides[1] 36 | 37 | val outRows = inRows ceilDiv rowStride 38 | val outCols = inCols ceilDiv colStride 39 | 40 | val elements = FloatArray(outCols * outRows * numChannels) 41 | 42 | var elementIndex = 0 43 | for (y in 0 until outRows) { 44 | val inY0 = y * rowStride 45 | val inMinY = Math.max(inY0 + inMinDy, 0) 46 | val inMaxY = Math.min(inY0 + inMaxDy, inRows - 1) 47 | 48 | for (x in 0 until outCols) { 49 | val inX0 = x * colStride 50 | val inMinX = Math.max(inX0 + inMinDx, 0) 51 | val inMaxX = Math.min(inX0 + inMaxDx, inCols - 1) 52 | 53 | for (c in 0 until numChannels) { 54 | var maxElement = Float.MIN_VALUE 55 | for (inY in inMinY..inMaxY) { 56 | for (inX in inMinX..inMaxX) { 57 | maxElement = Math.max(maxElement, this.elements[(inY * inCols + inX) * numChannels + c]) 58 | } 59 | } 60 | elements[elementIndex++] = maxElement 61 | } 62 | } 63 | } 64 | 65 | return Tensor(Shape(outRows, outCols, numChannels), elements) 66 | } 67 | 68 | fun Tensor.conv2d(filter: Tensor, strides: IntArray): Tensor { 69 | val inChannels = filter.shape.dimensions[2] 70 | 71 | assert({ shape.dimensions.size == 3 }, { "`shape.dimensions.size` must be 3: ${shape.dimensions.size}" }) 72 | assert({ filter.shape.dimensions.size == 4 }, { "`filter.shape.dimensions.size` must be 4: ${filter.shape.dimensions.size}" }) 73 | assert({ strides.size == 3 }, { "`strides.size` must be 3: ${ strides.size }" }) 74 | assert({ strides[2] == 1 } , { "`strides[2]` != 1 is not supported: ${ strides[2] }" }) 75 | assert({ shape.dimensions[2] == inChannels }, { "The number of channels of this tensor and the filter are not compatible: ${shape.dimensions[2]} != ${inChannels}" }) 76 | 77 | val inRows = shape.dimensions[0] 78 | val inCols = shape.dimensions[1] 79 | 80 | val filterHeight = filter.shape.dimensions[0] 81 | val filterWidth = filter.shape.dimensions[1] 82 | 83 | val inMinDy = -(filterHeight - 1) / 2 84 | val inMaxDy = inMinDy + filterHeight - 1 85 | val inMinDx = -(filterWidth - 1) / 2 86 | val inMaxDx = inMinDx + filterWidth - 1 87 | 88 | val rowStride = strides[0] 89 | val colStride = strides[1] 90 | 91 | val outRows = shape.dimensions[0] ceilDiv rowStride 92 | val outCols = shape.dimensions[1] ceilDiv colStride 93 | val outChannels = filter.shape.dimensions[3] 94 | 95 | val elements = FloatArray(outCols * outRows * outChannels) 96 | 97 | for (y in 0 until outRows) { 98 | val inY0 = y * rowStride 99 | val inMinY = Math.max(inY0 + inMinDy, 0) 100 | val inMaxY = Math.min(inY0 + inMaxDy, inRows - 1) 101 | 102 | for (x in 0 until outCols) { 103 | val inX0 = x * colStride 104 | val inMinX = Math.max(inX0 + inMinDx, 0) 105 | val inMaxX = Math.min(inX0 + inMaxDx, inCols - 1) 106 | 107 | val inYOffset = inY0 + inMinDy 108 | val inXOffset = inX0 + inMinDx 109 | 110 | for (inY in inMinY..inMaxY) { 111 | for (inX in inMinX..inMaxX) { 112 | matmuladd( 113 | inChannels, outChannels, 114 | (inY * inCols + inX) * inChannels, this.elements, 115 | ((inY - inYOffset) * filterWidth + (inX - inXOffset)) * inChannels * outChannels, filter.elements, 116 | (y * outCols + x) * outChannels, elements 117 | ) 118 | } 119 | } 120 | } 121 | } 122 | 123 | return Tensor(Shape(outRows, outCols, outChannels), elements) 124 | } 125 | 126 | internal fun matmuladd(inCols1Rows2: Int, outCols: Int, o1: Int, vec: FloatArray, o2: Int, mat: FloatArray, oo: Int, out: FloatArray) { 127 | for (i in 0 until inCols1Rows2) { 128 | var elementIndex = oo 129 | val left = vec[i + o1] 130 | for (c in 0 until outCols) { 131 | out[elementIndex] += left * mat[i * outCols + c + o2] 132 | elementIndex++ 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/jp/co/qoncept/tensorkotlin/Utils.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | internal fun floatArrayOf(size: Int, repeatedValue: Float): FloatArray { 4 | val array = FloatArray(size) 5 | array.fill(repeatedValue) 6 | return array 7 | } 8 | 9 | internal inline fun zipMap(a: FloatArray, b: FloatArray, operation: (Float, Float) -> Float): FloatArray { 10 | val result = FloatArray(a.size) 11 | for (i in a.indices) { 12 | result[i] = operation(a[i], b[i]) 13 | } 14 | return result 15 | } 16 | 17 | internal inline fun zipMapRepeat(a: FloatArray, infiniteB: FloatArray, operation: (Float, Float) -> Float): FloatArray { 18 | val result = FloatArray(a.size) 19 | for (i in a.indices) { 20 | result[i] = operation(a[i], infiniteB[i % infiniteB.size]) 21 | } 22 | return result 23 | } 24 | 25 | internal inline fun zipFold(a: FloatArray, b: FloatArray, initial: R, operation: (R, Float, Float) -> R): R { 26 | var result: R = initial 27 | for (i in a.indices) { 28 | result = operation(result, a[i], b[i]) 29 | } 30 | return result 31 | } 32 | 33 | internal inline fun zipFold(a: IntArray, b: IntArray, initial: R, operation: (R, Int, Int) -> R): R { 34 | var result: R = initial 35 | for (i in a.indices) { 36 | result = operation(result, a[i], b[i]) 37 | } 38 | return result 39 | } 40 | 41 | internal inline fun FloatArray.map(transform: (Float) -> Float): FloatArray { 42 | val result = FloatArray(size) 43 | for (i in indices) { 44 | result[i] = transform(this[i]) 45 | } 46 | return result 47 | } 48 | 49 | internal inline fun Array.map(transform: (T) -> Int): IntArray { 50 | val result = IntArray(size) 51 | for (i in indices) { 52 | result[i] = transform(this[i]) 53 | } 54 | return result 55 | } 56 | 57 | internal fun IntArray.endsWith(suffix: IntArray): Boolean { 58 | if (size < suffix.size) { return false } 59 | val offset = size - suffix.size 60 | for (i in suffix.indices) { 61 | if (this[offset + i] != suffix[i]) { 62 | return false 63 | } 64 | } 65 | return true 66 | } 67 | 68 | internal inline fun assert(value: () -> Boolean, lazyMessage: () -> Any) { 69 | if (Tensor::class.java.desiredAssertionStatus()) { 70 | if (!value()) { 71 | val message = lazyMessage() 72 | throw AssertionError(message) 73 | } 74 | } 75 | } 76 | 77 | internal infix fun Int.ceilDiv(rhs: Int): Int { 78 | return (this + rhs - 1) / rhs 79 | } -------------------------------------------------------------------------------- /test/jp/co/qoncept/tensorkotlin/MatmulPerformanceTest.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import org.testng.annotations.Test 4 | 5 | private val dimension = 10000 6 | 7 | private fun createMatrix(): Tensor { 8 | val elements = FloatArray(dimension * dimension).map { Math.random().toFloat() } 9 | return Tensor(Shape(dimension, dimension), elements) 10 | } 11 | 12 | private fun createVector(): Tensor { 13 | val elements = FloatArray(1 * dimension).map { Math.random().toFloat() } 14 | return Tensor(Shape(1, dimension), elements) 15 | } 16 | 17 | class MatmulPerformanceTest { 18 | @Test 19 | fun testMatmul(){ 20 | val W = createMatrix() 21 | val x = createVector() 22 | 23 | measureBlock { 24 | x.matmul(W) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /test/jp/co/qoncept/tensorkotlin/TensorKotlinSample.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import org.testng.Assert.* 4 | import org.testng.annotations.Test 5 | 6 | class TensorKotlinSample { 7 | @Test 8 | fun testSample() { 9 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) // [[1, 2, 3], [4, 5, 6]] 10 | val b = Tensor(Shape(2, 3), floatArrayOf(7, 8, 9, 10, 11, 12)) // [[7, 8, 9], 10, 11, 12]] 11 | 12 | val x = a[1, 2] // 6.0f 13 | val sub = a[0..1, 1..2] // [[2, 3], [5, 6]] 14 | 15 | val sum = a + b // [[8, 10, 12], [14, 16, 18]] 16 | val mul = a * b // [[7, 16, 27], [40, 55, 72]] 17 | 18 | val c = Tensor(Shape(3, 1), floatArrayOf(7, 8, 9)) // [[7], [8], [9]] 19 | val matmul = a.matmul(c) // [[50], [122]] 20 | 21 | val zeros = Tensor(Shape(2, 3, 4)) 22 | val ones = Tensor(Shape(2, 3, 4), 1.0f) 23 | 24 | assertEquals(x, 6.0f) 25 | assertEquals(sub, Tensor(Shape(2, 2), floatArrayOf(2, 3, 5, 6))) 26 | assertEquals(sum, Tensor(Shape(2, 3), floatArrayOf(8, 10, 12, 14, 16, 18))) 27 | assertEquals(mul, Tensor(Shape(2, 3), floatArrayOf(7, 16, 27, 40, 55, 72))) 28 | assertEquals(matmul, Tensor(Shape(2, 1), floatArrayOf(50, 122))) 29 | assertEquals(zeros, Tensor(Shape(2, 3, 4), floatArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))) 30 | assertEquals(ones, Tensor(Shape(2, 3, 4), floatArrayOf(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1))) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/jp/co/qoncept/tensorkotlin/TensorNNTest.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import org.testng.Assert.* 4 | import org.testng.annotations.Test 5 | 6 | class TensorNNTest { 7 | @Test 8 | fun testMaxPool() { 9 | run { 10 | val a = Tensor(Shape(2,3,1), floatArrayOf(0,1,2,3,4,5)) 11 | var r = a.maxPool(intArrayOf(1,3,1), intArrayOf(1,1,1)) 12 | assertEquals(Tensor(Shape(2,3,1), floatArrayOf(1,2,2,4,5,5)), r) 13 | } 14 | 15 | run { 16 | val b = Tensor(Shape(2,2,2), floatArrayOf(0,1,2,3,4,5,6,7)) 17 | 18 | run { 19 | val r = b.maxPool(intArrayOf(1,2,1), intArrayOf(1,1,1)) 20 | assertEquals(Tensor(Shape(2,2,2), floatArrayOf(2, 3, 2, 3, 6, 7, 6, 7)), r) 21 | } 22 | 23 | run { 24 | val r = b.maxPool(intArrayOf(1,2,1), intArrayOf(1,2,1)) 25 | assertEquals(Tensor(Shape(2,1,2), floatArrayOf(2, 3, 6, 7)), r) 26 | } 27 | } 28 | } 29 | 30 | @Test 31 | fun testConv2d() { 32 | run { 33 | val a = Tensor(Shape(2,4,1), floatArrayOf(1,2,3,4,5,6,7,8)) 34 | run { 35 | val filter = Tensor(Shape(2,1,1,2), floatArrayOf(1,2,1,2)) 36 | val result = a.conv2d(filter, intArrayOf(1,1,1)) 37 | assertEquals(Tensor(Shape(2,4,2), floatArrayOf(6,12,8,16,10,20,12,24,5,10,6,12,7,14,8,16)), result) 38 | } 39 | 40 | run { 41 | val filter = Tensor(Shape(1,1,1,5), floatArrayOf(1,2,1,2,3)) 42 | val result = a.conv2d(filter, intArrayOf(1,1,1)) 43 | assertEquals(Tensor(Shape(2,4,5), floatArrayOf(1,2,1,2,3,2,4,2,4,6,3,6,3,6,9,4,8,4,8,12,5,10,5,10,15,6,12,6,12,18,7,14,7,14,21,8,16,8,16,24)), result) 44 | } 45 | } 46 | 47 | run { 48 | val a = Tensor(Shape(2,2,4), floatArrayOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)) 49 | val filter = Tensor(Shape(1,1,4,2), floatArrayOf(1,2,1,2,3,2,1,1)) 50 | val result = a.conv2d(filter, intArrayOf(1,1,1)) 51 | assertEquals(Tensor(Shape(2,2,2), floatArrayOf(16, 16, 40, 44, 64, 72, 88, 100)), result) 52 | } 53 | 54 | run { 55 | val a = Tensor(Shape(4,2,2), floatArrayOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)) 56 | val filter = Tensor(Shape(2,2,2,1), floatArrayOf(1,2,1,2,3,2,1,1)) 57 | val result = a.conv2d(filter, intArrayOf(2,2,1)) 58 | assertEquals(Tensor(Shape(2,1,1), floatArrayOf(58,162)), result) 59 | } 60 | 61 | run { 62 | val a = Tensor(Shape(4,4,1), floatArrayOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)) 63 | val filter = Tensor(Shape(3,3,1,1), floatArrayOf(1,2,1,2,3,2,1,1,1)) 64 | val result = a.conv2d(filter, intArrayOf(3,3,1)) 65 | assertEquals(Tensor(Shape(2,2,1), floatArrayOf(18,33,95,113)), result) 66 | } 67 | 68 | run { 69 | val a = Tensor(Shape(1,3,1), floatArrayOf(1,2,3)) 70 | val filter = Tensor(Shape(1,3,1,2), floatArrayOf(1,1,2,2,3,3)) 71 | val result = a.conv2d(filter, intArrayOf(1,1,1)) 72 | assertEquals(Tensor(Shape(1,3,2), floatArrayOf(8 ,8, 14, 14, 8, 8)), result) 73 | } 74 | 75 | run { 76 | val a = Tensor(Shape(10, 10, 1), naturalNumbers(10 * 10)) 77 | val filter = Tensor(Shape(5, 5, 1, 32), naturalNumbers(5 * 5 * 32)) 78 | val result = a.conv2d(filter, intArrayOf(1, 1, 1)) 79 | assertEquals(Shape(10, 10, 32), result.shape) 80 | assertEquals(66816.0f, result[0, 0, 0]) 81 | assertEquals(66915.0f, result[0, 0, 1]) 82 | assertEquals(67014.0f, result[0, 0, 2]) 83 | assertEquals(69687.0f, result[0, 0, 29]) 84 | assertEquals(69786.0f, result[0, 0, 30]) 85 | assertEquals(69885.0f, result[0, 0, 31]) 86 | assertEquals(90560.0f, result[0, 1, 0]) 87 | assertEquals(114880.0f, result[0, 2, 0]) 88 | assertEquals(155680.0f, result[0, 7, 0]) 89 | assertEquals(124160.0f, result[0, 8, 0]) 90 | assertEquals(92736.0f, result[0, 9, 0]) 91 | assertEquals(184824.0f, result[9, 9, 29]) 92 | assertEquals(185616.0f, result[9, 9, 30]) 93 | assertEquals(186408.0f, result[9, 9, 31]) 94 | } 95 | 96 | run { 97 | val a = Tensor(Shape(64, 43, 1), naturalNumbers(64 * 43)) 98 | val filter = Tensor(Shape(5, 5, 1, 32), naturalNumbers(5 * 5 * 32)) 99 | val result = a.conv2d(filter, intArrayOf(1, 1, 1)) 100 | assertEquals(Shape(64, 43, 32), result.shape) 101 | assertEquals(269568.0f, result[0, 0, 0]) 102 | assertEquals(269964.0f, result[0, 0, 1]) 103 | assertEquals(270360.0f, result[0, 0, 2]) 104 | assertEquals(281052.0f, result[0, 0, 29]) 105 | assertEquals(281448.0f, result[0, 0, 30]) 106 | assertEquals(281844.0f, result[0, 0, 31]) 107 | assertEquals(354560.0f, result[0, 1, 0]) 108 | assertEquals(436960.0f, result[0, 2, 0]) 109 | assertEquals(747040.0f, result[0, 40, 0]) 110 | assertEquals(584576.0f, result[0, 41, 0]) 111 | assertEquals(428544.0f, result[0, 42, 0]) 112 | assertEquals(5425695.0f, result[63, 42, 29]) 113 | assertEquals(5450058.0f, result[63, 42, 30]) 114 | assertEquals(5474421.0f, result[63, 42, 31]) 115 | } 116 | 117 | run { 118 | val a = Tensor(Shape(64, 43, 1), naturalNumbers(64 * 43).map { 1.0f / (it + 1)}) 119 | val filter = Tensor(Shape(5, 5, 1, 32), naturalNumbers(5 * 5 * 32). map { 1.0f / (it + 1) }) 120 | val result = a.conv2d(filter, intArrayOf(1, 1, 1)) 121 | assertEquals(Shape(64, 43, 32), result.shape) 122 | assertEquals(4.70107934e-03f, result[0, 0, 0]) 123 | assertEquals(4.68956726e-03f, result[0, 0, 1]) 124 | assertEquals(4.67811199e-03f, result[0, 0, 2]) 125 | assertEquals(4.38879896e-03f, result[0, 0, 29]) 126 | assertEquals(4.37877374e-03f, result[0, 0, 30]) 127 | assertEquals(4.36879462e-03f, result[0, 0, 31]) 128 | assertEquals(5.70829911e-03f, result[0, 1, 0]) 129 | assertEquals(6.72411174e-03f, result[0, 2, 0]) 130 | assertEquals(4.89232189e-04f, result[0, 40, 0]) 131 | assertEquals(4.00262536e-04f, result[0, 41, 0]) 132 | assertEquals(3.07742739e-04f, result[0, 42, 0]) 133 | assertEquals(3.04842852e-05f, result[63, 42, 29]) 134 | assertEquals(2.99116928e-05f, result[63, 42, 30]) 135 | assertEquals(2.93684534e-05f, result[63, 42, 31]) 136 | } 137 | 138 | run { 139 | val a = Tensor(Shape(43, 64, 1), naturalNumbers(43 * 64).map { 1.0f / (it + 1)}) 140 | val filter = Tensor(Shape(5, 5, 1, 32), naturalNumbers(5 * 5 * 32). map { 1.0f / (it + 1) }) 141 | val result = a.conv2d(filter, intArrayOf(1, 1, 1)) 142 | assertEquals(Shape(43, 64, 32), result.shape) 143 | assertEquals(4.64918977e-03f, result[0, 0, 0]) 144 | assertEquals(4.63776244e-03f, result[0, 0, 1]) 145 | assertEquals(4.62639052e-03f, result[0, 0, 2]) 146 | assertEquals(4.33925306e-03f, result[0, 0, 29]) 147 | assertEquals(4.32930561e-03f, result[0, 0, 30]) 148 | assertEquals(4.31940285e-03f, result[0, 0, 31]) 149 | assertEquals(5.63816028e-03f, result[0, 1, 0]) 150 | assertEquals(6.63504843e-03f, result[0, 2, 0]) 151 | assertEquals(3.24019609e-04f, result[0, 61, 0]) 152 | assertEquals(2.66118324e-04f, result[0, 62, 0]) 153 | assertEquals(2.05358563e-04f, result[0, 63, 0]) 154 | assertEquals(3.08850504e-05f, result[42, 63, 29]) 155 | assertEquals(3.03035958e-05f, result[42, 63, 30]) 156 | assertEquals(2.97519691e-05f, result[42, 63, 31]) 157 | } 158 | } 159 | 160 | @Test 161 | fun testMatmuladd() { 162 | run { 163 | val a = floatArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) 164 | val b = floatArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) 165 | val out = FloatArray(15) 166 | matmuladd(3, 2, 2, a, 5, b, 7, out) 167 | /* 168 | | 5 6 | 169 | | 2 3 4| | 7 8 | 170 | | 9 10 | 171 | */ 172 | assertEquals(floatArrayOf(0, 0, 0, 0, 0, 0, 0, 67, 76, 0, 0, 0, 0, 0, 0).toList(), out.toList()) 173 | } 174 | 175 | run { 176 | val a = floatArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) 177 | val b = floatArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) 178 | val out = FloatArray(15) 179 | matmuladd(3, 2, 2, a, 5, b, 7, out) 180 | matmuladd(3, 2, 2, a, 5, b, 7, out) 181 | /* 182 | | 5 6 | | 5 6 | 183 | | 2 3 4| | 7 8 | + | 2 3 4| | 7 8 | 184 | | 9 10 | | 9 10 | 185 | */ 186 | assertEquals(floatArrayOf(0, 0, 0, 0, 0, 0, 0, 134, 152, 0, 0, 0, 0, 0, 0).toList(), out.toList()) 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /test/jp/co/qoncept/tensorkotlin/TensorTest.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | import org.testng.Assert.* 4 | import org.testng.annotations.Test 5 | 6 | class TensorTest { 7 | @Test 8 | fun testIndex() { 9 | run { 10 | val a = Tensor(Shape()) 11 | assertEquals(0, a.index(intArrayOf())) 12 | } 13 | 14 | run { 15 | val a = Tensor(Shape(7)) 16 | assertEquals(3, a.index(intArrayOf(3))) 17 | } 18 | 19 | run { 20 | val a = Tensor(Shape(5, 7)) 21 | assertEquals(9, a.index(intArrayOf(1, 2))) 22 | } 23 | 24 | run { 25 | val a = Tensor(Shape(5, 7, 11)) 26 | assertEquals(244, a.index(intArrayOf(3, 1, 2))) 27 | } 28 | } 29 | 30 | @Test 31 | fun testGetByRanges() { 32 | run { 33 | val a = Tensor(Shape(5, 5, 5), (1..125).map { it.toFloat() }.toFloatArray()) 34 | val b = a[1..3, 2..4, 1..2] 35 | assertEquals(Tensor(Shape(3, 3, 2), floatArrayOf(37, 38, 42, 43, 47, 48, 62, 63, 67, 68, 72, 73, 87, 88, 92, 93, 97, 98)), b) 36 | } 37 | } 38 | 39 | @Test 40 | fun testPlus() { 41 | run { 42 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) 43 | val b = Tensor(Shape(2, 3), floatArrayOf(7, 8, 9, 10, 11, 12)) 44 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(8, 10, 12, 14, 16, 18)), a + b) 45 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(8, 10, 12, 14, 16, 18)), b + a) 46 | } 47 | 48 | run { 49 | val a = Tensor(Shape(2, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 50 | val b = Tensor(Shape(2), floatArrayOf(100, 200)) 51 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(101, 202, 103, 204, 105, 206, 107, 208, 109, 210, 111, 212)), a + b) 52 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(101, 202, 103, 204, 105, 206, 107, 208, 109, 210, 111, 212)), b + a) 53 | } 54 | 55 | run { 56 | val a = Tensor(Shape(2, 1, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 57 | val b = Tensor(Shape(3, 2), floatArrayOf(100, 200, 300, 400, 500, 600)) 58 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(101, 202, 303, 404, 505, 606, 107, 208, 309, 410, 511, 612)), a + b) 59 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(101, 202, 303, 404, 505, 606, 107, 208, 309, 410, 511, 612)), b + a) 60 | } 61 | } 62 | 63 | @Test 64 | fun testMinus() { 65 | run { 66 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) 67 | val b = Tensor(Shape(2, 3), floatArrayOf(12, 11, 10, 9, 8, 7)) 68 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(-11, -9, -7, -5, -3, -1)), a - b) 69 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(11, 9, 7, 5, 3, 1)), b - a) 70 | } 71 | 72 | run { 73 | val a = Tensor(Shape(2, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 74 | val b = Tensor(Shape(2), floatArrayOf(100, 200)) 75 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(-99, -198, -97, -196, -95, -194, -93, -192, -91, -190, -89, -188)), a - b) 76 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(99, 198, 97, 196, 95, 194, 93, 192, 91, 190, 89, 188)), b - a) 77 | } 78 | 79 | run { 80 | val a = Tensor(Shape(2, 1, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 81 | val b = Tensor(Shape(3, 2), floatArrayOf(100, 200, 300, 400, 500, 600)) 82 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(-99, -198, -297, -396, -495, -594, -93, -192, -291, -390, -489, -588)), a - b) 83 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(99, 198, 297, 396, 495, 594, 93, 192, 291, 390, 489, 588)), b - a) 84 | } 85 | } 86 | 87 | @Test 88 | fun testTimes() { 89 | run { 90 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) 91 | val b = Tensor(Shape(2, 3), floatArrayOf(7, 8, 9, 10, 11, 12)) 92 | val r = a * b 93 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(7, 16, 27, 40, 55, 72)), r) 94 | } 95 | 96 | run { 97 | val a = Tensor(Shape(2, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 98 | val b = Tensor(Shape(2), floatArrayOf(10, 100)) 99 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(10, 200, 30, 400, 50, 600, 70, 800, 90, 1000, 110, 1200)), a * b) 100 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(10, 200, 30, 400, 50, 600, 70, 800, 90, 1000, 110, 1200)), b * a) 101 | } 102 | 103 | run { 104 | val a = Tensor(Shape(2, 1, 3, 2), floatArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) 105 | val b = Tensor(Shape(3, 2), floatArrayOf(10, 100, 1000, -10, -100, -1000)) 106 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(10, 200, 3000, -40, -500, -6000, 70, 800, 9000, -100, -1100, -12000)), a * b) 107 | assertEquals(Tensor(Shape(2, 1, 3, 2), floatArrayOf(10, 200, 3000, -40, -500, -6000, 70, 800, 9000, -100, -1100, -12000)), b * a) 108 | } 109 | } 110 | 111 | @Test 112 | fun testDiv() { 113 | run { 114 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) 115 | val b = Tensor(Shape(2, 3), floatArrayOf(2, 4, 8, 16, 32, 64)) 116 | val r = a / b 117 | assertEquals(Tensor(Shape(2, 3), floatArrayOf(0.5f, 0.5f, 0.375f, 0.25f, 0.15625f, 0.09375f)), r) 118 | } 119 | 120 | run { 121 | val a = Tensor(Shape(2, 3, 2), floatArrayOf(2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096)) 122 | val b = Tensor(Shape(2), floatArrayOf(8, 2)) 123 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(0.25f, 2.0f, 1.0f, 8.0f, 4.0f, 32.0f, 16.0f, 128.0f, 64.0f, 512.0f, 256.0f, 2048.0f)), a / b) 124 | assertEquals(Tensor(Shape(2, 3, 2), floatArrayOf(4.0f, 0.5f, 1.0f, 0.125f, 0.25f, 0.03125f, 0.0625f, 0.0078125f, 0.015625f, 0.001953125f, 0.00390625f, 0.00048828125f)), b / a) 125 | } 126 | 127 | run { 128 | val a = Tensor(Shape(3, 1, 2, 2), floatArrayOf(2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096)) 129 | val b = Tensor(Shape(2, 2), floatArrayOf(8, 2, -8, -2)) 130 | assertEquals(Tensor(Shape(3, 1, 2, 2), floatArrayOf(0.25f, 2.0f, -1.0f, -8.0f, 4.0f, 32.0f, -16.0f, -128.0f, 64.0f, 512.0f, -256.0f, -2048.0f)), a / b) 131 | assertEquals(Tensor(Shape(3, 1, 2, 2), floatArrayOf(4.0f, 0.5f, -1.0f, -0.125f, 0.25f, 0.03125f, -0.0625f, -0.0078125f, 0.015625f, 0.001953125f, -0.00390625f, -0.00048828125f)), b / a) 132 | } 133 | } 134 | 135 | @Test 136 | fun testMatmul() { 137 | run { 138 | val a = Tensor(Shape(2, 3), floatArrayOf(1, 2, 3, 4, 5, 6)) 139 | val b = Tensor(Shape(3, 4), floatArrayOf(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)) 140 | val r = a.matmul(b) 141 | assertEquals(Tensor(Shape(2, 4), floatArrayOf(74, 80, 86, 92, 173, 188, 203, 218)), r) 142 | } 143 | } 144 | 145 | @Test 146 | fun testEquals() { 147 | run { 148 | val a = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 149 | val b = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 150 | assertTrue(a == b) 151 | } 152 | 153 | run { 154 | val a = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 155 | val b = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 17)) 156 | assertFalse(a == b) 157 | } 158 | 159 | run { 160 | val a = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 161 | val b = Tensor(Shape(3, 2), floatArrayOf(2, 3, 5, 7, 11, 17)) 162 | assertFalse(a == b) 163 | } 164 | 165 | run { 166 | val a = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 167 | val b = Tensor(Shape(2, 2), floatArrayOf(2, 3, 5, 7)) 168 | assertFalse(a == b) 169 | } 170 | 171 | run { 172 | val a = Tensor(Shape(2, 3), floatArrayOf(2, 3, 5, 7, 11, 13)) 173 | val b = Tensor(Shape(), floatArrayOf()) 174 | assertFalse(a == b) 175 | } 176 | 177 | run { 178 | val a = Tensor(Shape(), floatArrayOf()) 179 | val b = Tensor(Shape(), floatArrayOf()) 180 | assertTrue(a == b) 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /test/jp/co/qoncept/tensorkotlin/TestUtils.kt: -------------------------------------------------------------------------------- 1 | package jp.co.qoncept.tensorkotlin 2 | 3 | 4 | internal fun floatArrayOf(vararg elements: Int): FloatArray { 5 | val result = FloatArray(elements.size) 6 | for (i in elements.indices) { 7 | result[i] = elements[i].toFloat() 8 | } 9 | return result 10 | } 11 | 12 | internal fun measureBlock(procedure: () -> Unit) { 13 | run { 14 | val element = Thread.currentThread().stackTrace[2] 15 | println("measureBlock: ${element.className}\$${element.methodName}") 16 | } 17 | 18 | val N = 10 19 | var total: Long = 0 20 | for (i in 1..N) { 21 | val begin = System.currentTimeMillis() 22 | procedure() 23 | val end = System.currentTimeMillis() 24 | val elapsed = end - begin 25 | 26 | println("${i}: ${elapsed / 1000.0} [s]") 27 | 28 | total += elapsed 29 | } 30 | 31 | println("avg: ${total / 1000.0 / N} [s]") 32 | println() 33 | } 34 | 35 | internal fun naturalNumbers(n: Int): FloatArray { 36 | val result = FloatArray(n) 37 | for (i in 0 until n) { 38 | result[i] = i.toFloat() 39 | } 40 | return result 41 | } --------------------------------------------------------------------------------