├── .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 | }
--------------------------------------------------------------------------------