From 68bd0ae0af3b7f57a50bac8ba3dfe7069801d2e7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 14 Nov 2018 23:26:38 +0300 Subject: [PATCH 1/2] Norm is a separate context --- build.gradle | 8 ++- kmath-core/build.gradle | 5 -- .../scientifik/kmath/linear/LinearAlgrebra.kt | 16 +++--- .../scientifik/kmath/operations/Fields.kt | 49 +++++++++-------- .../kmath/operations/OptionalOperations.kt | 8 ++- .../scientifik/kmath/structures/Buffers.kt | 3 +- .../kmath/structures/ExtendedNDField.kt | 39 ++++++++++++++ .../scientifik/kmath/structures/NDField.kt | 7 +-- .../kmath/structures/RealNDField.kt | 46 ---------------- .../kmath/operations/RealFieldTest.kt | 4 +- ...ealNDFieldTest.kt => NumberNDFieldTest.kt} | 23 +++++--- kmath-io/build.gradle | 53 +++++++++++++++++++ kmath-jmh/build.gradle | 5 -- settings.gradle | 3 ++ 14 files changed, 168 insertions(+), 101 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt delete mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt rename kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/{RealNDFieldTest.kt => NumberNDFieldTest.kt} (68%) create mode 100644 kmath-io/build.gradle diff --git a/build.gradle b/build.gradle index 51d476ed6..4bd2464a9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ buildscript { - ext.kotlin_version = '1.3.0' + ext.kotlin_version = '1.3.10' repositories { + mavenCentral() jcenter() } @@ -17,6 +18,11 @@ allprojects { group = 'scientifik' version = '0.0.1-SNAPSHOT' + + repositories { + mavenCentral() + jcenter() + } } if(file('artifactory.gradle').exists()){ diff --git a/kmath-core/build.gradle b/kmath-core/build.gradle index 18be067e6..c63e518fc 100644 --- a/kmath-core/build.gradle +++ b/kmath-core/build.gradle @@ -2,11 +2,6 @@ plugins { id 'kotlin-multiplatform' } -repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } - mavenCentral() -} - kotlin { targets { fromPreset(presets.jvm, 'jvm') diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt index e585b884d..39931b676 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt @@ -1,9 +1,6 @@ package scientifik.kmath.linear -import scientifik.kmath.operations.DoubleField -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.SpaceElement +import scientifik.kmath.operations.* import scientifik.kmath.structures.* /** @@ -70,8 +67,6 @@ abstract class MatrixSpace(val rows: Int, val columns: Int, val field: result = 31 * result + field.hashCode() return result } - - } infix fun Matrix.dot(b: Matrix): Matrix = this.context.multiply(this, b) @@ -191,11 +186,11 @@ interface Vector : SpaceElement, VectorSpace>, Buffer, typealias NDFieldFactory = (IntArray) -> NDField internal fun genericNDFieldFactory(field: Field): NDFieldFactory = { index -> GenericNDField(index, field) } -internal val realNDFieldFactory: NDFieldFactory = { index -> GenericNDField(index, DoubleField) } +internal val realNDFieldFactory: NDFieldFactory = { index -> ExtendedNDField(index, DoubleField) } /** - * NDArray-based implementation of vector space. By default uses slow [SimpleNDField], but could be overridden with custom [NDField] factory. + * NDArray-based implementation of vector space. By default uses slow [GenericNDField], but could be overridden with custom [NDField] factory. */ class ArrayMatrixSpace( rows: Int, @@ -318,3 +313,8 @@ fun Vector.toMatrix(): Matrix { return Matrix.of(size, 1, context.field) { i, _ -> get(i) } } +object VectorL2Norm: Norm, Double> { + override fun norm(arg: Vector): Double { + return kotlin.math.sqrt(arg.sumByDouble { it.toDouble() }) + } +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt index eeba76222..5702803fc 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt @@ -2,10 +2,20 @@ package scientifik.kmath.operations import kotlin.math.pow +/** + * Advanced Number-like field that implements basic operations + */ +interface ExtendedField : + Field, + TrigonometricOperations, + PowerOperations, + ExponentialOperations + + /** * Field for real values */ -object RealField : Field, TrigonometricOperations, PowerOperations, ExponentialOperations { +object RealField : ExtendedField, Norm { override val zero: Real = Real(0.0) override fun add(a: Real, b: Real): Real = Real(a.value + b.value) override val one: Real = Real(1.0) @@ -13,33 +23,24 @@ object RealField : Field, TrigonometricOperations, PowerOperations { - /* - * The class uses composition instead of inheritance since Double is final - */ - - override fun toByte(): Byte = value.toByte() - override fun toChar(): Char = value.toChar() - override fun toDouble(): Double = value - override fun toFloat(): Float = value.toFloat() - override fun toInt(): Int = value.toInt() - override fun toLong(): Long = value.toLong() - override fun toShort(): Short = value.toShort() +class Real(val value: Double) : FieldElement { //values are dynamically calculated to save memory override val self @@ -53,19 +54,21 @@ data class Real(val value: Double) : Number(), FieldElement { /** * A field for double without boxing. Does not produce appropriate field element */ -object DoubleField : Field, TrigonometricOperations, PowerOperations, ExponentialOperations { +object DoubleField : ExtendedField, Norm { override val zero: Double = 0.0 override fun add(a: Double, b: Double): Double = a + b override fun multiply(a: Double, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") b: Double): Double = a * b override val one: Double = 1.0 override fun divide(a: Double, b: Double): Double = a / b - override fun sin(arg: Double): Double = kotlin.math.sin(arg) + override fun sin(arg: Double): Double = kotlin.math.sin(arg) override fun cos(arg: Double): Double = kotlin.math.cos(arg) - override fun power(arg: Double, pow: Double): Double = arg.pow(pow) + override fun power(arg: Double, pow: Double): Double = arg.pow(pow) - override fun exp(arg: Double): Double =kotlin.math.exp(arg) + override fun exp(arg: Double): Double = kotlin.math.exp(arg) - override fun ln(arg: Double): Double = kotlin.math.ln(arg) + override fun ln(arg: Double): Double = kotlin.math.ln(arg) + + override fun norm(arg: Double): Double = kotlin.math.abs(arg) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt index 7628cb4fc..9e5c8a801 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -45,4 +45,10 @@ interface ExponentialOperations { } fun >> exp(arg: T): T = arg.context.exp(arg) -fun >> ln(arg: T): T = arg.context.ln(arg) \ No newline at end of file +fun >> ln(arg: T): T = arg.context.ln(arg) + +interface Norm { + fun norm(arg: T): R +} + +fun >, R> norm(arg: T): R = arg.context.norm(arg) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 04983ef85..1be3514f2 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -42,6 +42,7 @@ inline class ListBuffer(private val list: MutableList) : MutableBuffer } class ArrayBuffer(private val array: Array) : MutableBuffer { + //Can't inline because array invariant override val size: Int get() = array.size @@ -56,7 +57,7 @@ class ArrayBuffer(private val array: Array) : MutableBuffer { override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } -class DoubleBuffer(private val array: DoubleArray) : MutableBuffer { +inline class DoubleBuffer(private val array: DoubleArray) : MutableBuffer { override val size: Int get() = array.size diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt new file mode 100644 index 000000000..6392aeec6 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ExtendedNDField.kt @@ -0,0 +1,39 @@ +package scientifik.kmath.structures + +import scientifik.kmath.operations.* + + +/** + * NDField that supports [ExtendedField] operations on its elements + */ +class ExtendedNDField(shape: IntArray, override val field: ExtendedField) : NDField(shape, field), + TrigonometricOperations>, + PowerOperations>, + ExponentialOperations> { + + override fun produceStructure(initializer: (IntArray) -> N): NDStructure { + return genericNdStructure(shape, initializer) + } + + override fun power(arg: NDArray, pow: Double): NDArray { + return arg.transform { d -> with(field){power(d,pow)} } + } + + override fun exp(arg: NDArray): NDArray { + return arg.transform { d -> with(field){exp(d)} } + } + + override fun ln(arg: NDArray): NDArray { + return arg.transform { d -> with(field){ln(d)} } + } + + override fun sin(arg: NDArray): NDArray { + return arg.transform { d -> with(field){sin(d)} } + } + + override fun cos(arg: NDArray): NDArray { + return arg.transform { d -> with(field){cos(d)} } + } +} + + diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt index 7b991200b..74bffa7ad 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt @@ -1,5 +1,6 @@ package scientifik.kmath.structures +import scientifik.kmath.operations.DoubleField import scientifik.kmath.operations.Field import scientifik.kmath.operations.FieldElement @@ -14,7 +15,7 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run * @param field - operations field defined on individual array element * @param T the type of the element contained in NDArray */ -abstract class NDField(val shape: IntArray, val field: Field) : Field> { +abstract class NDField(val shape: IntArray, open val field: Field) : Field> { abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure @@ -173,7 +174,7 @@ object NDArrays { * Create a platform-optimized NDArray of doubles */ fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDArray { - return RealNDField(shape).produce(initializer) + return ExtendedNDField(shape, DoubleField).produce(initializer) } fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray { @@ -188,7 +189,7 @@ object NDArrays { return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } } - inline fun produceReal(shape: IntArray, block: RealNDField.() -> RealNDArray) = RealNDField(shape).run(block) + inline fun produceReal(shape: IntArray, block: ExtendedNDField.() -> NDArray) = ExtendedNDField(shape, DoubleField).run(block) // /** // * Simple boxing NDField diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt deleted file mode 100644 index 2ba5fd706..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ /dev/null @@ -1,46 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.DoubleField -import scientifik.kmath.operations.ExponentialOperations -import scientifik.kmath.operations.PowerOperations -import scientifik.kmath.operations.TrigonometricOperations -import kotlin.math.* - -typealias RealNDArray = NDArray - - -class RealNDField(shape: IntArray) : NDField(shape, DoubleField), - TrigonometricOperations, - PowerOperations, - ExponentialOperations { - - override fun produceStructure(initializer: (IntArray) -> Double): NDStructure { - return genericNdStructure(shape, initializer) - } - - override fun power(arg: RealNDArray, pow: Double): RealNDArray { - return arg.transform { d -> d.pow(pow) } - } - - override fun exp(arg: RealNDArray): RealNDArray { - return arg.transform { d -> exp(d) } - } - - override fun ln(arg: RealNDArray): RealNDArray { - return arg.transform { d -> ln(d) } - } - - override fun sin(arg: RealNDArray): RealNDArray { - return arg.transform { d -> sin(d) } - } - - override fun cos(arg: RealNDArray): RealNDArray { - return arg.transform { d -> cos(d) } - } - - fun abs(arg: RealNDArray): RealNDArray { - return arg.transform { d -> abs(d) } - } -} - - diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt index b07158cc3..551bf0ee4 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -7,8 +7,8 @@ class RealFieldTest { @Test fun testSqrt() { val sqrt = with(RealField) { - sqrt(25 * one) + sqrt( 25 * one) } - assertEquals(5.0, sqrt.toDouble()) + assertEquals(5.0, sqrt.value) } } \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/RealNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt similarity index 68% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/RealNDFieldTest.kt rename to kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt index 179fb5ade..844535fa8 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/RealNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/NumberNDFieldTest.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import scientifik.kmath.linear.Vector +import scientifik.kmath.linear.VectorL2Norm +import scientifik.kmath.operations.Norm import scientifik.kmath.structures.NDArrays.produceReal import scientifik.kmath.structures.NDArrays.real2DArray import kotlin.math.abs @@ -7,7 +10,7 @@ import kotlin.math.pow import kotlin.test.Test import kotlin.test.assertEquals -class RealNDFieldTest { +class NumberNDFieldTest { val array1 = real2DArray(3, 3) { i, j -> (i + j).toDouble() } val array2 = real2DArray(3, 3) { i, j -> (i - j).toDouble() } @@ -46,14 +49,22 @@ class RealNDFieldTest { @Test fun testLibraryFunction() { val abs: (Double) -> Double = ::abs - val result = abs(array1) - assertEquals(10.0, result[1,1]) + val result = abs(array2) + assertEquals(2.0, result[0,2]) + } + + object L2Norm: Norm, Double> { + override fun norm(arg: NDArray): Double { + return kotlin.math.sqrt(arg.sumByDouble { it.second.toDouble() }) + } } @Test - fun testAbs(){ - val res = produceReal(array1.shape){ - 1 + abs(array1) + exp(array2) + fun testInternalContext(){ + produceReal(array1.shape){ + with(L2Norm) { + 1 + norm(array1) + exp(array2) + } } } } diff --git a/kmath-io/build.gradle b/kmath-io/build.gradle new file mode 100644 index 000000000..d0d93262e --- /dev/null +++ b/kmath-io/build.gradle @@ -0,0 +1,53 @@ +plugins { + id 'kotlin-multiplatform' +} + +kotlin { + targets { + fromPreset(presets.jvm, 'jvm') + fromPreset(presets.js, 'js') + // For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64 + // For Linux, preset should be changed to e.g. presets.linuxX64 + // For MacOS, preset should be changed to e.g. presets.macosX64 + //fromPreset(presets.mingwX64, 'mingw') + } + sourceSets { + commonMain { + dependencies { + api project(":kmath-core") + implementation 'org.jetbrains.kotlin:kotlin-stdlib-common' + } + } + commonTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-common' + implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common' + } + } + jvmMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + } + } + jvmTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test' + implementation 'org.jetbrains.kotlin:kotlin-test-junit' + } + } + jsMain { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-js' + } + } + jsTest { + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-test-js' + } + } +// mingwMain { +// } +// mingwTest { +// } + } +} diff --git a/kmath-jmh/build.gradle b/kmath-jmh/build.gradle index a16bf3022..97337cb40 100644 --- a/kmath-jmh/build.gradle +++ b/kmath-jmh/build.gradle @@ -4,11 +4,6 @@ plugins { id "me.champeau.gradle.jmh" version "0.4.7" } -repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } - mavenCentral() -} - dependencies { implementation project(':kmath-core') jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' diff --git a/settings.gradle b/settings.gradle index df4ccc99e..ddcbef5dd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ pluginManagement { repositories { mavenCentral() + jcenter() maven { url = 'https://plugins.gradle.org/m2/' } } } @@ -9,5 +10,7 @@ enableFeaturePreview('GRADLE_METADATA') rootProject.name = 'kmath' include ':kmath-core' +include ':kmath-io' + include ':kmath-jmh' From dcf8f4c6fd38d7e4c72d5dfe09b8556a43bab9f6 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 15 Nov 2018 11:07:51 +0300 Subject: [PATCH 2/2] inline class bug --- .../commonMain/kotlin/scientifik/kmath/operations/Fields.kt | 4 ++-- .../kotlin/scientifik/kmath/operations/RealFieldTest.kt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt index 5702803fc..3e4f47869 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Fields.kt @@ -38,9 +38,9 @@ object RealField : ExtendedField, Norm { /** * Real field element wrapping double. * - * TODO inline does not work due to compiler bug. Waiting for fix + * TODO inline does not work due to compiler bug. Waiting for fix for KT-27586 */ -class Real(val value: Double) : FieldElement { +inline class Real(val value: Double) : FieldElement { //values are dynamically calculated to save memory override val self diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt index 551bf0ee4..0d33204df 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -6,6 +6,8 @@ import kotlin.test.assertEquals class RealFieldTest { @Test fun testSqrt() { + + //fails because KT-27586 val sqrt = with(RealField) { sqrt( 25 * one) }