From d4cd4fd426120dd532343db6d6117f4aaa6c6036 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 26 Jun 2019 19:16:22 +0300 Subject: [PATCH 1/8] Build fix for deploy --- .../main/kotlin/npm-multiplatform.gradle.kts | 21 +++++++++++++++++++ .../src/main/kotlin/npm-publish.gradle.kts | 16 -------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts b/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts index 20b096ab8..7cf92afaf 100644 --- a/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts +++ b/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts @@ -1,3 +1,7 @@ +import com.jfrog.bintray.gradle.tasks.BintrayUploadTask +import org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact +import org.jfrog.gradle.plugin.artifactory.task.ArtifactoryTask + plugins { kotlin("multiplatform") `maven-publish` @@ -80,3 +84,20 @@ kotlin { } } + +//workaround for bintray and artifactory +project.tasks.filter { it is ArtifactoryTask || it is BintrayUploadTask }.forEach { + it.doFirst { + project.configure { + publications.filterIsInstance() + .forEach { publication -> + val moduleFile = project.buildDir.resolve("publications/${publication.name}/module.json") + if (moduleFile.exists()) { + publication.artifact(object : FileBasedMavenArtifact(moduleFile) { + override fun getDefaultExtension() = "module" + }) + } + } + } + } +} diff --git a/buildSrc/src/main/kotlin/npm-publish.gradle.kts b/buildSrc/src/main/kotlin/npm-publish.gradle.kts index 175f15f0b..ee55b996f 100644 --- a/buildSrc/src/main/kotlin/npm-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/npm-publish.gradle.kts @@ -93,22 +93,6 @@ bintray { } } -//workaround for bintray -tasks.withType { - doFirst { - publishing.publications - .filterIsInstance() - .forEach { publication -> - val moduleFile = buildDir.resolve("publications/${publication.name}/module.json") - if (moduleFile.exists()) { - publication.artifact(object : FileBasedMavenArtifact(moduleFile) { - override fun getDefaultExtension() = "module" - }) - } - } - } -} - artifactory { val artifactoryUser: String? by project val artifactoryPassword: String? by project From 7f3dd95e7284b2ff6c30b865f6e422da25caa929 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 17 Jul 2019 14:21:37 +0300 Subject: [PATCH 2/8] Moved to scientifik plugin. Removed depricated Vector --- build.gradle.kts | 18 ++- buildSrc/build.gradle.kts | 20 --- buildSrc/settings.gradle.kts | 0 buildSrc/src/main/kotlin/Versions.kt | 9 -- .../src/main/kotlin/dokka-publish.gradle.kts | 75 ----------- buildSrc/src/main/kotlin/js-test.gradle.kts | 44 ------- .../main/kotlin/npm-multiplatform.gradle.kts | 103 --------------- .../src/main/kotlin/npm-publish.gradle.kts | 120 ------------------ examples/build.gradle.kts | 17 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- gradlew.bat | 2 +- kmath-core/build.gradle.kts | 2 +- .../kmath/linear/LUPDecomposition.kt | 2 +- .../scientifik/kmath/linear/LinearAlgrebra.kt | 16 +-- .../scientifik/kmath/linear/RealVector.kt | 48 +++++++ .../kotlin/scientifik/kmath/linear/Vector.kt | 71 ----------- .../scientifik/kmath/linear/MatrixTest.kt | 10 +- kmath-coroutines/build.gradle.kts | 16 +-- .../kotlin/scientifik/kmath/chains/Chain.kt | 37 ++++-- kmath-histograms/build.gradle.kts | 2 +- .../kmath/histogram/RealHistogram.kt | 6 +- .../histogram/MultivariateHistogramTest.kt | 10 +- .../kmath/histogram/UnivariateHistogram.kt | 4 +- kmath-koma/build.gradle.kts | 2 +- kmath-memory/build.gradle.kts | 2 +- kmath-prob/build.gradle.kts | 10 +- settings.gradle.kts | 7 +- 28 files changed, 136 insertions(+), 521 deletions(-) delete mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/settings.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/Versions.kt delete mode 100644 buildSrc/src/main/kotlin/dokka-publish.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/js-test.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/npm-publish.gradle.kts create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt delete mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt diff --git a/build.gradle.kts b/build.gradle.kts index edc4bed7b..3584b790a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,18 @@ -val kmathVersion by extra("0.1.3") +plugins { + id("scientifik.mpp") version "0.1.3" apply false + id("scientifik.publish") version "0.1.3" apply false + id("kotlinx-atomicfu") version "0.12.9" apply false +} + +val kmathVersion by extra("0.1.4-dev-1") + +val bintrayRepo by extra("scientifik") +val githubProject by extra("kmath") allprojects { repositories { jcenter() - maven("https://kotlin.bintray.com/kotlinx") + maven("https://dl.bintray.com/kotlin/kotlinx") } group = "scientifik" @@ -11,8 +20,7 @@ allprojects { } subprojects { - apply(plugin = "dokka-publish") if (name.startsWith("kmath")) { - apply(plugin = "npm-publish") + apply(plugin = "scientifik.publish") } -} +} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index 318189162..000000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - gradlePluginPortal() - jcenter() -} - -val kotlinVersion = "1.3.31" - -// Add plugins used in buildSrc as dependencies, also we should specify version only here -dependencies { - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") - implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.9.6") - implementation("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.9.18") - implementation("com.moowork.gradle:gradle-node-plugin:1.3.1") - implementation("org.openjfx:javafx-plugin:0.0.7") -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts deleted file mode 100644 index e69de29bb..000000000 diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt deleted file mode 100644 index 883af1203..000000000 --- a/buildSrc/src/main/kotlin/Versions.kt +++ /dev/null @@ -1,9 +0,0 @@ -// Instead of defining runtime properties and use them dynamically -// define version in buildSrc and have autocompletion and compile-time check -// Also dependencies itself can be moved here -object Versions { - val ioVersion = "0.1.8" - val coroutinesVersion = "1.2.1" - val atomicfuVersion = "0.12.6" - val serializationVersion = "0.11.0" -} diff --git a/buildSrc/src/main/kotlin/dokka-publish.gradle.kts b/buildSrc/src/main/kotlin/dokka-publish.gradle.kts deleted file mode 100644 index b7b48fb68..000000000 --- a/buildSrc/src/main/kotlin/dokka-publish.gradle.kts +++ /dev/null @@ -1,75 +0,0 @@ -import org.jetbrains.dokka.gradle.DokkaTask -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension - -plugins { - id("org.jetbrains.dokka") - `maven-publish` -} - -afterEvaluate { - - extensions.findByType()?.apply{ - val dokka by tasks.getting(DokkaTask::class) { - outputFormat = "html" - outputDirectory = "$buildDir/javadoc" - jdkVersion = 8 - - kotlinTasks { - // dokka fails to retrieve sources from MPP-tasks so we only define the jvm task - listOf(tasks.getByPath("compileKotlinJvm")) - } - sourceRoot { - // assuming only single source dir - path = sourceSets["commonMain"].kotlin.srcDirs.first().toString() - platforms = listOf("Common") - } - // although the JVM sources are now taken from the task, - // we still define the jvm source root to get the JVM marker in the generated html - sourceRoot { - // assuming only single source dir - path = sourceSets["jvmMain"].kotlin.srcDirs.first().toString() - platforms = listOf("JVM") - } - } - - val kdocJar by tasks.registering(Jar::class) { - group = JavaBasePlugin.DOCUMENTATION_GROUP - dependsOn(dokka) - archiveClassifier.set("javadoc") - from("$buildDir/javadoc") - } - - configure { - - targets.all { - val publication = publications.findByName(name) as MavenPublication - - // Patch publications with fake javadoc - publication.artifact(kdocJar.get()) - } - } - } - - - extensions.findByType()?.apply{ - val dokka by tasks.getting(DokkaTask::class) { - outputFormat = "html" - outputDirectory = "$buildDir/javadoc" - jdkVersion = 8 - } - - val kdocJar by tasks.registering(Jar::class) { - group = JavaBasePlugin.DOCUMENTATION_GROUP - dependsOn(dokka) - archiveClassifier.set("javadoc") - from("$buildDir/javadoc") - } - - configure { - publications.filterIsInstance().forEach { publication -> - publication.artifact(kdocJar.get()) - } - } - } -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/js-test.gradle.kts b/buildSrc/src/main/kotlin/js-test.gradle.kts deleted file mode 100644 index 61759a28a..000000000 --- a/buildSrc/src/main/kotlin/js-test.gradle.kts +++ /dev/null @@ -1,44 +0,0 @@ -import com.moowork.gradle.node.npm.NpmTask -import com.moowork.gradle.node.task.NodeTask -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile - -plugins { - id("com.moowork.node") - kotlin("multiplatform") -} - -node { - nodeModulesDir = file("$buildDir/node_modules") -} - -val compileKotlinJs by tasks.getting(Kotlin2JsCompile::class) -val compileTestKotlinJs by tasks.getting(Kotlin2JsCompile::class) - -val populateNodeModules by tasks.registering(Copy::class) { - dependsOn(compileKotlinJs) - from(compileKotlinJs.destinationDir) - - kotlin.js().compilations["test"].runtimeDependencyFiles.forEach { - if (it.exists() && !it.isDirectory) { - from(zipTree(it.absolutePath).matching { include("*.js") }) - } - } - - into("$buildDir/node_modules") -} - -val installMocha by tasks.registering(NpmTask::class) { - setWorkingDir(buildDir) - setArgs(listOf("install", "mocha")) -} - -val runMocha by tasks.registering(NodeTask::class) { - dependsOn(compileTestKotlinJs, populateNodeModules, installMocha) - setScript(file("$buildDir/node_modules/mocha/bin/mocha")) - setArgs(listOf(compileTestKotlinJs.outputFile)) -} - -tasks["jsTest"].dependsOn(runMocha) - - diff --git a/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts b/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts deleted file mode 100644 index 7cf92afaf..000000000 --- a/buildSrc/src/main/kotlin/npm-multiplatform.gradle.kts +++ /dev/null @@ -1,103 +0,0 @@ -import com.jfrog.bintray.gradle.tasks.BintrayUploadTask -import org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact -import org.jfrog.gradle.plugin.artifactory.task.ArtifactoryTask - -plugins { - kotlin("multiplatform") - `maven-publish` -} - - -kotlin { - jvm { - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - } - } - } - - js { - compilations.all { - kotlinOptions { - metaInfo = true - sourceMap = true - sourceMapEmbedSources = "always" - moduleKind = "commonjs" - } - } - - compilations.named("main") { - kotlinOptions { - main = "call" - } - } - } - - sourceSets { - val commonMain by getting { - dependencies { - api(kotlin("stdlib")) - } - } - val commonTest by getting { - dependencies { - implementation(kotlin("test-common")) - implementation(kotlin("test-annotations-common")) - } - } - val jvmMain by getting { - dependencies { - api(kotlin("stdlib-jdk8")) - } - } - val jvmTest by getting { - dependencies { - implementation(kotlin("test")) - implementation(kotlin("test-junit")) - } - } - val jsMain by getting { - dependencies { - api(kotlin("stdlib-js")) - } - } - val jsTest by getting { - dependencies { - implementation(kotlin("test-js")) - } - } - } - - targets.all { - sourceSets.all { - languageSettings.progressiveMode = true - languageSettings.enableLanguageFeature("InlineClasses") - } - } - - // Apply JS test configuration - val runJsTests by ext(false) - - if (runJsTests) { - apply(plugin = "js-test") - } - -} - -//workaround for bintray and artifactory -project.tasks.filter { it is ArtifactoryTask || it is BintrayUploadTask }.forEach { - it.doFirst { - project.configure { - publications.filterIsInstance() - .forEach { publication -> - val moduleFile = project.buildDir.resolve("publications/${publication.name}/module.json") - if (moduleFile.exists()) { - publication.artifact(object : FileBasedMavenArtifact(moduleFile) { - override fun getDefaultExtension() = "module" - }) - } - } - } - } -} diff --git a/buildSrc/src/main/kotlin/npm-publish.gradle.kts b/buildSrc/src/main/kotlin/npm-publish.gradle.kts deleted file mode 100644 index ee55b996f..000000000 --- a/buildSrc/src/main/kotlin/npm-publish.gradle.kts +++ /dev/null @@ -1,120 +0,0 @@ -@file:Suppress("UnstableApiUsage") - -import com.jfrog.bintray.gradle.tasks.BintrayUploadTask -import groovy.lang.GroovyObject -import org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact -import org.jfrog.gradle.plugin.artifactory.dsl.PublisherConfig -import org.jfrog.gradle.plugin.artifactory.dsl.ResolverConfig - -// Old bintray.gradle script converted to real Gradle plugin (precompiled script plugin) -// It now has own dependencies and support type safe accessors -// Syntax is pretty close to what we had in Groovy -// (excluding Property.set and bintray dynamic configs) - -plugins { - `maven-publish` - id("com.jfrog.bintray") - id("com.jfrog.artifactory") -} - -val vcs = "https://github.com/mipt-npm/kmath" -val bintrayRepo = "https://bintray.com/mipt-npm/scientifik" - -// Configure publishing -publishing { - repositories { - maven(bintrayRepo) - } - - // Process each publication we have in this project - publications.filterIsInstance().forEach { publication -> - - // use type safe pom config GSL instead of old dynamic - publication.pom { - name.set(project.name) - description.set(project.description) - url.set(vcs) - - licenses { - license { - name.set("The Apache Software License, Version 2.0") - url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - distribution.set("repo") - } - } - developers { - developer { - id.set("MIPT-NPM") - name.set("MIPT nuclear physics methods laboratory") - organization.set("MIPT") - organizationUrl.set("http://npm.mipt.ru") - } - - } - scm { - url.set(vcs) - } - } - } - -} - -bintray { - user = findProperty("bintrayUser") as? String ?: System.getenv("BINTRAY_USER") - key = findProperty("bintrayApiKey") as? String? ?: System.getenv("BINTRAY_API_KEY") - publish = true - override = true // for multi-platform Kotlin/Native publishing - - // We have to use delegateClosureOf because bintray supports only dynamic groovy syntax - // this is a problem of this plugin - pkg.apply { - userOrg = "mipt-npm" - repo = "scientifik" - name = project.name - issueTrackerUrl = "$vcs/issues" - setLicenses("Apache-2.0") - vcsUrl = vcs - version.apply { - name = project.version.toString() - vcsTag = project.version.toString() - released = java.util.Date().toString() - } - } - - //workaround bintray bug - afterEvaluate { - setPublications(*publishing.publications.names.toTypedArray()) - } - - tasks { - bintrayUpload { - dependsOn(publishToMavenLocal) - } - } -} - -artifactory { - val artifactoryUser: String? by project - val artifactoryPassword: String? by project - val artifactoryContextUrl = "http://npm.mipt.ru:8081/artifactory" - - setContextUrl(artifactoryContextUrl)//The base Artifactory URL if not overridden by the publisher/resolver - publish(delegateClosureOf { - repository(delegateClosureOf { - setProperty("repoKey", "gradle-dev-local") - setProperty("username", artifactoryUser) - setProperty("password", artifactoryPassword) - }) - - defaults(delegateClosureOf { - invokeMethod("publications", arrayOf("jvm", "js", "kotlinMultiplatform", "metadata")) - }) - }) - resolve(delegateClosureOf { - repository(delegateClosureOf { - setProperty("repoKey", "gradle-dev") - setProperty("username", artifactoryUser) - setProperty("password", artifactoryPassword) - }) - }) -} diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 7f6bec31f..98d8607e2 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -1,12 +1,11 @@ -import org.jetbrains.gradle.benchmarks.JvmBenchmarkTarget import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { java kotlin("jvm") - kotlin("plugin.allopen") version "1.3.31" - id("org.jetbrains.gradle.benchmarks.plugin") version "0.1.7-dev-24" + kotlin("plugin.allopen") version "1.3.41" + id("kotlinx.benchmark") version "0.2.0-dev-2" } configure { @@ -16,7 +15,8 @@ configure { repositories { maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("http://dl.bintray.com/kyonifer/maven") - maven("https://dl.bintray.com/orangy/maven") + maven ("https://dl.bintray.com/orangy/maven") + mavenCentral() } @@ -30,9 +30,9 @@ dependencies { implementation(project(":kmath-commons")) implementation(project(":kmath-koma")) implementation("com.kyonifer:koma-core-ejml:0.12") - implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.5") + implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:${Scientifik.ioVersion}") - implementation("org.jetbrains.gradle.benchmarks:runtime:0.1.7-dev-24") + implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-2") "benchmarksCompile"(sourceSets.main.get().compileClasspath) @@ -43,10 +43,7 @@ benchmark { // Setup configurations targets { // This one matches sourceSet name above - register("benchmarks") { - this as JvmBenchmarkTarget - jmhVersion = "1.21" - } + register("benchmarks") } configurations { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5f1b1201a..4b7e1f3d3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0ab5..8e25e6c19 100755 --- a/gradlew +++ b/gradlew @@ -7,7 +7,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, diff --git a/gradlew.bat b/gradlew.bat index 9991c5032..9618d8d96 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 25507968c..34941064b 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - `npm-multiplatform` + id("scientifik.mpp") } kotlin.sourceSets { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt index 910ed4551..87e0ef027 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUPDecomposition.kt @@ -183,7 +183,7 @@ fun LUPDecomposition.solve(type: KClass, matrix: Matrix): Mat elementContext.run { // Apply permutations to b - val bp = create { i, j -> zero } + val bp = create { _, _ -> zero } for (row in 0 until pivot.size) { val bpRow = bp.row(row) 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 7e631f5fc..0456ffebb 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt @@ -3,10 +3,12 @@ package scientifik.kmath.linear import scientifik.kmath.operations.Field import scientifik.kmath.operations.Norm import scientifik.kmath.operations.RealField +import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.VirtualBuffer import scientifik.kmath.structures.asSequence +typealias Point = Buffer /** * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors @@ -17,20 +19,6 @@ interface LinearSolver { fun inverse(a: Matrix): Matrix } -/** - * Convert vector to array (copying content of array) - */ -fun Array.toVector(field: Field) = Vector.generic(size, field) { this[it] } - -fun DoubleArray.toVector() = Vector.real(this.size) { this[it] } -fun List.toVector() = Vector.real(this.size) { this[it] } - -object VectorL2Norm : Norm, Double> { - override fun norm(arg: Point): Double = - kotlin.math.sqrt(arg.asSequence().sumByDouble { it.toDouble() }) -} - -typealias RealVector = Vector typealias RealMatrix = Matrix /** diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt new file mode 100644 index 000000000..d3cf07e79 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/RealVector.kt @@ -0,0 +1,48 @@ +package scientifik.kmath.linear + +import scientifik.kmath.operations.Field +import scientifik.kmath.operations.Norm +import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.SpaceElement +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.DoubleBuffer +import scientifik.kmath.structures.asBuffer +import scientifik.kmath.structures.asSequence + +fun DoubleArray.asVector() = RealVector(this.asBuffer()) +fun List.asVector() = RealVector(this.asBuffer()) + + +object VectorL2Norm : Norm, Double> { + override fun norm(arg: Point): Double = + kotlin.math.sqrt(arg.asSequence().sumByDouble { it.toDouble() }) +} + +inline class RealVector(val point: Point) : + SpaceElement, RealVector, VectorSpace>, Point { + override val context: VectorSpace get() = space(point.size) + + override fun unwrap(): Point = point + + override fun Point.wrap(): RealVector = RealVector(this) + + override val size: Int get() = point.size + + override fun get(index: Int): Double = point[index] + + override fun iterator(): Iterator = point.iterator() + + companion object { + + private val spaceCache = HashMap>() + + inline operator fun invoke(dim:Int, initalizer: (Int)-> Double) = RealVector(DoubleBuffer(dim, initalizer)) + + operator fun invoke(vararg values: Double) = values.asVector() + + fun space(dim: Int) = + spaceCache.getOrPut(dim) { + BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) } + } + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt deleted file mode 100644 index 18a0021c3..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt +++ /dev/null @@ -1,71 +0,0 @@ -package scientifik.kmath.linear - -import scientifik.kmath.operations.RealField -import scientifik.kmath.operations.Space -import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.asSequence -import kotlin.jvm.JvmName - -typealias Point = Buffer - - -fun > BufferVectorSpace.produceElement(initializer: (Int) -> T): Vector = - BufferVector(this, produce(initializer)) - -@JvmName("produceRealElement") -fun BufferVectorSpace.produceElement(initializer: (Int) -> Double): Vector = - BufferVector(this, produce(initializer)) - -/** - * A point coupled to the linear space - */ -@Deprecated("Use VectorContext instead") -interface Vector> : SpaceElement, Vector, VectorSpace>, Point { - override val size: Int get() = context.size - - override operator fun plus(b: Point): Vector = context.add(this, b).wrap() - override operator fun minus(b: Point): Vector = context.add(this, context.multiply(b, -1.0)).wrap() - override operator fun times(k: Number): Vector = context.multiply(this, k.toDouble()).wrap() - override operator fun div(k: Number): Vector = context.multiply(this, 1.0 / k.toDouble()).wrap() - - companion object { - /** - * Create vector with custom field - */ - fun > generic(size: Int, field: S, initializer: (Int) -> T): Vector = - VectorSpace.buffered(size, field).produceElement(initializer) - - fun real(size: Int, initializer: (Int) -> Double): Vector = - VectorSpace.real(size).produceElement(initializer) - - fun ofReal(vararg elements: Double): Vector = - VectorSpace.real(elements.size).produceElement { elements[it] } - - } -} - -@Deprecated("Use VectorContext instead") -data class BufferVector>(override val context: VectorSpace, val buffer: Buffer) : - Vector { - - init { - if (context.size != buffer.size) { - error("Array dimension mismatch") - } - } - - override fun get(index: Int): T { - return buffer[index] - } - - override fun unwrap(): Point = this - - override fun Point.wrap(): Vector = BufferVector(context, this) - - override fun iterator(): Iterator = (0 until size).map { buffer[it] }.iterator() - - override fun toString(): String = - this.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() } -} - diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt index 5370ae960..de13c9888 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/MatrixTest.kt @@ -8,15 +8,15 @@ class MatrixTest { @Test fun testSum() { - val vector1 = Vector.real(5) { it.toDouble() } - val vector2 = Vector.real(5) { 5 - it.toDouble() } + val vector1 = RealVector(5) { it.toDouble() } + val vector2 = RealVector(5) { 5 - it.toDouble() } val sum = vector1 + vector2 assertEquals(5.0, sum[2]) } @Test fun testVectorToMatrix() { - val vector = Vector.real(5) { it.toDouble() } + val vector = RealVector(5) { it.toDouble() } val matrix = vector.asMatrix() assertEquals(4.0, matrix[4, 0]) } @@ -31,8 +31,8 @@ class MatrixTest { @Test fun testDot() { - val vector1 = Vector.real(5) { it.toDouble() } - val vector2 = Vector.real(5) { 5 - it.toDouble() } + val vector1 = RealVector(5) { it.toDouble() } + val vector2 = RealVector(5) { 5 - it.toDouble() } val matrix1 = vector1.asMatrix() val matrix2 = vector2.asMatrix().transpose() diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index 6ffaf4df6..f54263072 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -1,26 +1,26 @@ plugins { - `npm-multiplatform` - id("kotlinx-atomicfu") version Versions.atomicfuVersion + id("scientifik.mpp") + id("kotlinx-atomicfu") } kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-core")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Versions.atomicfuVersion}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Scientifik.coroutinesVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Scientifik.atomicfuVersion}") } } jvmMain { dependencies { - api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu:${Scientifik.atomicfuVersion}") } } jsMain { dependencies { - api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Versions.atomicfuVersion}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Scientifik.atomicfuVersion}") } } } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 317cdcea0..e3f141b44 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -20,6 +20,8 @@ import kotlinx.atomicfu.atomic import kotlinx.atomicfu.updateAndGet import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock /** @@ -64,16 +66,22 @@ class SimpleChain(private val gen: suspend () -> R) : Chain { */ class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { - constructor(seedValue: R, gen: suspend (R) -> R) : this({ seedValue }, gen) + //constructor(seedValue: R, gen: suspend (R) -> R) : this({ seedValue }, gen) - private val value = atomic(null) + private val mutex = Mutex() + + private var value: R? = null override suspend fun next(): R { - return value.updateAndGet { prev -> gen(prev ?: seed()) }!! + mutex.withLock { + val newValue = gen(value ?: seed()) + value = newValue + return newValue + } } override fun fork(): Chain { - return MarkovChain(seed = { value.value ?: seed() }, gen = gen) + return MarkovChain(seed = { value ?: seed() }, gen = gen) } } @@ -89,17 +97,22 @@ class StatefulChain( private val gen: suspend S.(R) -> R ) : Chain { - constructor(state: S, seedValue: R, forkState: ((S) -> S), gen: suspend S.(R) -> R) : this( - state, - { seedValue }, - forkState, - gen - ) +// constructor(state: S, seedValue: R, forkState: ((S) -> S), gen: suspend S.(R) -> R) : this( +// state, +// { seedValue }, +// forkState, +// gen +// ) + private val mutex = Mutex() - private val atomicValue = atomic(null) + private var value: R? = null override suspend fun next(): R { - return atomicValue.updateAndGet { prev -> state.gen(prev ?: state.seed()) }!! + mutex.withLock { + val newValue = state.gen(value ?: state.seed()) + value = newValue + return newValue + } } override fun fork(): Chain { diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 7eaa5e174..39aa833ad 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - `npm-multiplatform` + id("scientifik.mpp") } kotlin.sourceSets.commonMain { diff --git a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt index 75296ba27..a2f0fc516 100644 --- a/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/scientifik/kmath/histogram/RealHistogram.kt @@ -1,7 +1,7 @@ package scientifik.kmath.histogram import scientifik.kmath.linear.Point -import scientifik.kmath.linear.toVector +import scientifik.kmath.linear.asVector import scientifik.kmath.operations.SpaceOperations import scientifik.kmath.structures.* import kotlin.math.floor @@ -132,8 +132,8 @@ class RealHistogram( */ fun fromRanges(vararg ranges: ClosedFloatingPointRange): RealHistogram { return RealHistogram( - ranges.map { it.start }.toVector(), - ranges.map { it.endInclusive }.toVector() + ranges.map { it.start }.asVector(), + ranges.map { it.endInclusive }.asVector() ) } diff --git a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt index 678b8eba7..4dc4dfc74 100644 --- a/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/scietifik/kmath/histogram/MultivariateHistogramTest.kt @@ -3,7 +3,7 @@ package scietifik.kmath.histogram import scientifik.kmath.histogram.RealHistogram import scientifik.kmath.histogram.fill import scientifik.kmath.histogram.put -import scientifik.kmath.linear.Vector +import scientifik.kmath.linear.RealVector import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals @@ -19,9 +19,9 @@ class MultivariateHistogramTest { ) histogram.put(0.55, 0.55) val bin = histogram.find { it.value.toInt() > 0 }!! - assertTrue { bin.contains(Vector.ofReal(0.55, 0.55)) } - assertTrue { bin.contains(Vector.ofReal(0.6, 0.5)) } - assertFalse { bin.contains(Vector.ofReal(-0.55, 0.55)) } + assertTrue { bin.contains(RealVector(0.55, 0.55)) } + assertTrue { bin.contains(RealVector(0.6, 0.5)) } + assertFalse { bin.contains(RealVector(-0.55, 0.55)) } } @Test @@ -39,7 +39,7 @@ class MultivariateHistogramTest { histogram.fill { repeat(n) { - yield(Vector.ofReal(nextDouble(), nextDouble(), nextDouble())) + yield(RealVector(nextDouble(), nextDouble(), nextDouble())) } } assertEquals(n, histogram.sumBy { it.value.toInt() }) diff --git a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt index 53c2da641..90b9aff5e 100644 --- a/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -1,7 +1,7 @@ package scientifik.kmath.histogram import scientifik.kmath.linear.RealVector -import scientifik.kmath.linear.toVector +import scientifik.kmath.linear.asVector import scientifik.kmath.structures.Buffer import java.util.* import kotlin.math.floor @@ -12,7 +12,7 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou //TODO add weighting override val value: Number get() = counter.sum() - override val center: RealVector get() = doubleArrayOf(position).toVector() + override val center: RealVector get() = doubleArrayOf(position).asVector() operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) diff --git a/kmath-koma/build.gradle.kts b/kmath-koma/build.gradle.kts index 9b07fdcf2..26955bca7 100644 --- a/kmath-koma/build.gradle.kts +++ b/kmath-koma/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - `npm-multiplatform` + id("scientifik.mpp") } repositories { diff --git a/kmath-memory/build.gradle.kts b/kmath-memory/build.gradle.kts index 3fb56989c..1f34a4f17 100644 --- a/kmath-memory/build.gradle.kts +++ b/kmath-memory/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - `npm-multiplatform` + id("scientifik.mpp") } diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 972b9bba0..3294469ab 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -1,25 +1,25 @@ plugins { - `npm-multiplatform` - id("kotlinx-atomicfu") version Versions.atomicfuVersion + id("scientifik.mpp") + id("kotlinx-atomicfu") } kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-coroutines")) - compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Versions.atomicfuVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Scientifik.atomicfuVersion}") } } jvmMain { dependencies { // https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple //api("org.apache.commons:commons-rng-sampling:1.2") - compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu:${Scientifik.atomicfuVersion}") } } jsMain { dependencies { - compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Versions.atomicfuVersion}") + compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Scientifik.atomicfuVersion}") } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 004b432fd..b45091a0b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,11 @@ pluginManagement { repositories { + mavenLocal() jcenter() gradlePluginPortal() maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/orangy/maven") + maven("https://dl.bintray.com/mipt-npm/scientifik") + maven("https://dl.bintray.com/kotlin/kotlinx") } resolutionStrategy { eachPlugin { @@ -11,7 +13,8 @@ pluginManagement { "kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") "kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") "kotlin2js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - //"org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") + "scientifik.mpp", "scientifik.publish" -> useModule("scientifik:gradle-tools:${requested.version}") } } } From 5a08faf0b6481f4de983b81cfc5c9580b6864c34 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 24 Aug 2019 08:23:02 +0300 Subject: [PATCH 3/8] Fix for #74. Removed square from complex --- .../kotlin/scientifik/kmath/operations/Complex.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 8f373b8de..79f9e3c35 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -26,7 +26,7 @@ object ComplexField : ExtendedFieldOperations, Field { Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) override fun divide(a: Complex, b: Complex): Complex { - val norm = b.square + val norm = b.re * b.re + b.im * b.im return Complex((a.re * b.re + a.im * b.im) / norm, (a.re * b.im - a.im * b.re) / norm) } @@ -35,11 +35,11 @@ object ComplexField : ExtendedFieldOperations, Field { override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2 override fun power(arg: Complex, pow: Number): Complex = - arg.abs.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) + arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta)) override fun exp(arg: Complex): Complex = exp(arg.re) * (cos(arg.im) + i * sin(arg.im)) - override fun ln(arg: Complex): Complex = ln(arg.abs) + i * atan2(arg.im, arg.re) + override fun ln(arg: Complex): Complex = ln(arg.r) + i * atan2(arg.im, arg.re) operator fun Double.plus(c: Complex) = add(this.toComplex(), c) @@ -62,7 +62,7 @@ data class Complex(val re: Double, val im: Double) : FieldElement { override val objectSize: Int = 16 @@ -82,15 +82,10 @@ data class Complex(val re: Double, val im: Double) : FieldElement Date: Sat, 24 Aug 2019 09:00:04 +0300 Subject: [PATCH 4/8] Build migrated to gradle-tools 1.4 --- build.gradle.kts | 4 +- kmath-commons/build.gradle.kts | 18 +----- .../commons/transform/Transformations.kt | 2 +- kmath-core/build.gradle.kts | 2 - .../scientifik/kmath/operations/Algebra.kt | 12 +++- .../scientifik/kmath/operations/functions.kt | 34 ++++++++++++ kmath-coroutines/build.gradle.kts | 1 - kmath-io/build.gradle | 55 ------------------- settings.gradle.kts | 1 + 9 files changed, 50 insertions(+), 79 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt delete mode 100644 kmath-io/build.gradle diff --git a/build.gradle.kts b/build.gradle.kts index 3584b790a..b4b3f032e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("scientifik.mpp") version "0.1.3" apply false - id("scientifik.publish") version "0.1.3" apply false + id("scientifik.mpp") version "0.1.4" apply false + id("scientifik.publish") version "0.1.4" apply false id("kotlinx-atomicfu") version "0.12.9" apply false } diff --git a/kmath-commons/build.gradle.kts b/kmath-commons/build.gradle.kts index ffdc27d43..9b446f872 100644 --- a/kmath-commons/build.gradle.kts +++ b/kmath-commons/build.gradle.kts @@ -1,6 +1,5 @@ plugins { - kotlin("jvm") - `maven-publish` + id("scientifik.jvm") } description = "Commons math binding for kmath" @@ -12,19 +11,4 @@ dependencies { api("org.apache.commons:commons-math3:3.6.1") testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit") -} - - -val sourcesJar by tasks.registering(Jar::class) { - classifier = "sources" - from(sourceSets.main.get().allSource) -} - -publishing { - publications { - register("jvm", MavenPublication::class) { - from(components["java"]) - artifact(sourcesJar.get()) - } - } } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt index ce51735fe..bcb3ea87b 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt @@ -11,7 +11,7 @@ import scientifik.kmath.structures.* /** - * + * Streaming and buffer transformations */ object Transformations { diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index 34941064b..092f3deb7 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -8,6 +8,4 @@ kotlin.sourceSets { api(project(":kmath-memory")) } } - //mingwMain {} - //mingwTest {} } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index ee9833623..03cbe6b55 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -1,7 +1,14 @@ package scientifik.kmath.operations +/** + * Marker interface for any algebra + */ +interface Algebra -interface SpaceOperations { +/** + * Space-like operations without neutral element + */ +interface SpaceOperations : Algebra { /** * Addition operation for two context elements */ @@ -38,6 +45,9 @@ interface Space : SpaceOperations { val zero: T } +/** + * Operations on ring without multiplication neutral element + */ interface RingOperations : SpaceOperations { /** * Multiplication for two field elements diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt new file mode 100644 index 000000000..a76167b43 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt @@ -0,0 +1,34 @@ +package scientifik.kmath.operations + +import kotlin.jvm.JvmName +import kotlin.reflect.KClass + +/** + * A suspendable univariate function defined in algebraic context + */ +interface UFunction> { + suspend operator fun C.invoke(arg: T): T +} + +suspend fun UFunction.invoke(arg: Double) = RealField.invoke(arg) + +/** + * A suspendable multivariate (N->1) function defined on algebraic context + */ +interface MFunction> { + /** + * The input dimension of the function + */ + val dimension: UInt + + suspend operator fun C.invoke(vararg args: T): T +} + +suspend fun MFunction.invoke(args: DoubleArray) = RealField.invoke(*args.toTypedArray()) +@JvmName("varargInvoke") +suspend fun MFunction.invoke(vararg args: Double) = RealField.invoke(*args.toTypedArray()) + + +interface ParametricUFunction> { + suspend operator fun C.invoke(arg: T, parameter: P): T +} \ No newline at end of file diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index f54263072..e01c61326 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("scientifik.mpp") - id("kotlinx-atomicfu") } kotlin.sourceSets { diff --git a/kmath-io/build.gradle b/kmath-io/build.gradle deleted file mode 100644 index 28fb7eee5..000000000 --- a/kmath-io/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id "org.jetbrains.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' - api "org.jetbrains.kotlinx:kotlinx-io:$ioVersion" - } - } - 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' - api "org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion" - } - } - 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/settings.gradle.kts b/settings.gradle.kts index b45091a0b..f7ef0fc42 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,5 +32,6 @@ include( ":kmath-commons", ":kmath-koma", ":kmath-prob", + ":kmath-io", ":examples" ) From 92b4e6f28cb7291efc60898b5b581bb92e294678 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 25 Aug 2019 10:51:25 +0300 Subject: [PATCH 5/8] Tools to evaluate statistic --- build.gradle.kts | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 4 +- .../misc/{Cumulative.kt => cumulative.kt} | 13 +-- .../kmath/{operations => misc}/functions.kt | 11 ++- .../kmath/operations/AlgebraExtensions.kt | 3 - .../scientifik/kmath/structures/Buffers.kt | 4 +- kmath-coroutines/build.gradle.kts | 4 +- .../kotlin/scientifik/kmath/chains/Chain.kt | 15 +--- .../scientifik/kmath/chains/flowExtra.kt | 27 ++++++ ...{CoroutinesExtra.kt => coroutinesExtra.kt} | 11 ++- .../scientifik/kmath/streaming/BufferFlow.kt | 4 - .../kmath/streaming/BufferFlowTest.kt | 8 +- .../kotlin/scientifik/memory/MemorySpec.kt | 4 +- kmath-prob/build.gradle.kts | 21 +---- .../scientifik/kmath/prob/RandomChain.kt | 6 +- .../kotlin/scientifik/kmath/prob/Statistic.kt | 88 +++++++++++++++++++ .../scientifik/kmath/prob/StatisticTest.kt | 28 ++++++ settings.gradle.kts | 13 ++- 19 files changed, 191 insertions(+), 81 deletions(-) rename kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/{Cumulative.kt => cumulative.kt} (86%) rename kmath-core/src/commonMain/kotlin/scientifik/kmath/{operations => misc}/functions.kt (82%) create mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt rename kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/{CoroutinesExtra.kt => coroutinesExtra.kt} (92%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt create mode 100644 kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index b4b3f032e..4e55432ee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,8 @@ plugins { - id("scientifik.mpp") version "0.1.4" apply false - id("scientifik.publish") version "0.1.4" apply false - id("kotlinx-atomicfu") version "0.12.9" apply false + id("scientifik.publish") version "0.1.6" apply false } -val kmathVersion by extra("0.1.4-dev-1") +val kmathVersion by extra("0.1.4-dev") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3d3..ef9a9e05e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 8e25e6c19..83f2acfdc 100755 --- a/gradlew +++ b/gradlew @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt similarity index 86% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt index 314696262..c3cfc448a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/Cumulative.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/cumulative.kt @@ -10,29 +10,32 @@ import kotlin.jvm.JvmName * @param R type of resulting iterable * @param initial lazy evaluated */ -fun Iterator.cumulative(initial: R, operation: (T, R) -> R): Iterator = object : Iterator { +fun Iterator.cumulative(initial: R, operation: (R, T) -> R): Iterator = object : Iterator { var state: R = initial override fun hasNext(): Boolean = this@cumulative.hasNext() override fun next(): R { - state = operation.invoke(this@cumulative.next(), state) + state = operation(state, this@cumulative.next()) return state } } -fun Iterable.cumulative(initial: R, operation: (T, R) -> R): Iterable = object : Iterable { +fun Iterable.cumulative(initial: R, operation: (R, T) -> R): Iterable = object : Iterable { override fun iterator(): Iterator = this@cumulative.iterator().cumulative(initial, operation) } -fun Sequence.cumulative(initial: R, operation: (T, R) -> R): Sequence = object : Sequence { +fun Sequence.cumulative(initial: R, operation: (R, T) -> R): Sequence = object : Sequence { override fun iterator(): Iterator = this@cumulative.iterator().cumulative(initial, operation) } -fun List.cumulative(initial: R, operation: (T, R) -> R): List = +fun List.cumulative(initial: R, operation: (R, T) -> R): List = this.iterator().cumulative(initial, operation).asSequence().toList() //Cumulative sum +/** + * Cumulative sum with custom space + */ fun Iterable.cumulativeSum(space: Space) = with(space) { cumulative(zero) { element: T, sum: T -> sum + element } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/functions.kt similarity index 82% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/functions.kt index a76167b43..cef8209ed 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/functions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/misc/functions.kt @@ -1,7 +1,8 @@ -package scientifik.kmath.operations +package scientifik.kmath.coroutines +import scientifik.kmath.operations.RealField +import scientifik.kmath.operations.SpaceOperations import kotlin.jvm.JvmName -import kotlin.reflect.KClass /** * A suspendable univariate function defined in algebraic context @@ -28,7 +29,9 @@ suspend fun MFunction.invoke(args: DoubleArray) = RealField.i @JvmName("varargInvoke") suspend fun MFunction.invoke(vararg args: Double) = RealField.invoke(*args.toTypedArray()) - +/** + * A suspendable univariate function with parameter + */ interface ParametricUFunction> { suspend operator fun C.invoke(arg: T, parameter: P): T -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt index 6dec8bd79..4e8dbf36d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/AlgebraExtensions.kt @@ -1,7 +1,4 @@ package scientifik.kmath.operations -import scientifik.kmath.structures.Buffer -import scientifik.kmath.structures.asSequence - fun Space.sum(data : Iterable): T = data.fold(zero) { left, right -> add(left,right) } fun Space.sum(data : Sequence): T = data.fold(zero) { left, right -> add(left, right) } \ 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 a38f09c8d..c63384fb9 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -69,9 +69,9 @@ interface Buffer { } } -fun Buffer.asSequence(): Sequence = iterator().asSequence() +fun Buffer.asSequence(): Sequence = Sequence(::iterator) -fun Buffer.asIterable(): Iterable = iterator().asSequence().asIterable() +fun Buffer.asIterable(): Iterable = asSequence().asIterable() interface MutableBuffer : Buffer { operator fun set(index: Int, value: T) diff --git a/kmath-coroutines/build.gradle.kts b/kmath-coroutines/build.gradle.kts index e01c61326..373d9b8ac 100644 --- a/kmath-coroutines/build.gradle.kts +++ b/kmath-coroutines/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("scientifik.mpp") + //id("scientifik.atomic") } kotlin.sourceSets { @@ -7,19 +8,16 @@ kotlin.sourceSets { dependencies { api(project(":kmath-core")) api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Scientifik.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Scientifik.atomicfuVersion}") } } jvmMain { dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu:${Scientifik.atomicfuVersion}") } } jsMain { dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}") - compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Scientifik.atomicfuVersion}") } } } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index e3f141b44..161dc3b7f 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -16,9 +16,6 @@ package scientifik.kmath.chains -import kotlinx.atomicfu.atomic -import kotlinx.atomicfu.updateAndGet -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -46,9 +43,7 @@ interface Chain { /** * Chain as a coroutine flow. The flow emit affects chain state and vice versa */ -@FlowPreview -val Chain.flow: Flow - get() = kotlinx.coroutines.flow.flow { while (true) emit(next()) } +fun Chain.flow(): Flow = kotlinx.coroutines.flow.flow { while (true) emit(next()) } fun Iterator.asChain(): Chain = SimpleChain { next() } fun Sequence.asChain(): Chain = iterator().asChain() @@ -66,8 +61,6 @@ class SimpleChain(private val gen: suspend () -> R) : Chain { */ class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { - //constructor(seedValue: R, gen: suspend (R) -> R) : this({ seedValue }, gen) - private val mutex = Mutex() private var value: R? = null @@ -97,12 +90,6 @@ class StatefulChain( private val gen: suspend S.(R) -> R ) : Chain { -// constructor(state: S, seedValue: R, forkState: ((S) -> S), gen: suspend S.(R) -> R) : this( -// state, -// { seedValue }, -// forkState, -// gen -// ) private val mutex = Mutex() private var value: R? = null diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt new file mode 100644 index 000000000..bfd16d763 --- /dev/null +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/flowExtra.kt @@ -0,0 +1,27 @@ +package scientifik.kmath.chains + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.scan +import kotlinx.coroutines.flow.scanReduce +import scientifik.kmath.operations.Space +import scientifik.kmath.operations.SpaceOperations + + +@ExperimentalCoroutinesApi +fun Flow.cumulativeSum(space: SpaceOperations): Flow = with(space) { + scanReduce { sum: T, element: T -> sum + element } +} + +@ExperimentalCoroutinesApi +fun Flow.mean(space: Space): Flow = with(space) { + class Accumulator(var sum: T, var num: Int) + + scan(Accumulator(zero, 0)) { sum, element -> + sum.apply { + this.sum += element + this.num += 1 + } + }.map { it.sum / it.num } +} \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt similarity index 92% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt rename to kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt index dc82d1881..fdde62304 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/coroutinesExtra.kt @@ -21,8 +21,8 @@ internal class LazyDeferred(val dispatcher: CoroutineDispatcher, val block: s suspend fun await(): T = deferred?.await() ?: error("Coroutine not started") } -@FlowPreview class AsyncFlow internal constructor(internal val deferredFlow: Flow>) : Flow { + @InternalCoroutinesApi override suspend fun collect(collector: FlowCollector) { deferredFlow.collect { collector.emit((it.await())) @@ -88,14 +88,13 @@ suspend fun AsyncFlow.collect(concurrency: Int, action: suspend (value: T }) } +@ExperimentalCoroutinesApi @FlowPreview -fun Flow.map( - dispatcher: CoroutineDispatcher, - concurrencyLevel: Int = 16, - bufferSize: Int = concurrencyLevel, +fun Flow.mapParallel( + dispatcher: CoroutineDispatcher = Dispatchers.Default, transform: suspend (T) -> R ): Flow { - return flatMapMerge(concurrencyLevel, bufferSize) { value -> + return flatMapMerge{ value -> flow { emit(transform(value)) } }.flowOn(dispatcher) } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index 85ce73c4b..cf4d4cc17 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -9,7 +9,6 @@ import scientifik.kmath.structures.DoubleBuffer /** * Create a [Flow] from buffer */ -@FlowPreview fun Buffer.asFlow() = iterator().asFlow() /** @@ -21,7 +20,6 @@ fun Flow>.spread(): Flow = flatMapConcat { it.asFlow() } /** * Collect incoming flow into fixed size chunks */ -@FlowPreview fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory): Flow> = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } val list = ArrayList(bufferSize) @@ -45,7 +43,6 @@ fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory): Flow< /** * Specialized flow chunker for real buffer */ -@FlowPreview fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } val array = DoubleArray(bufferSize) @@ -69,7 +66,6 @@ fun Flow.chunked(bufferSize: Int): Flow = flow { * Map a flow to a moving window buffer. The window step is one. * In order to get different steps, one could use skip operation. */ -@FlowPreview fun Flow.windowed(window: Int): Flow> = flow { require(window > 1) { "Window size must be more than one" } val ringBuffer = RingBuffer.boxing(window) diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt index 4fd397993..af2d6cd43 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.collect import org.junit.Test import scientifik.kmath.coroutines.async import scientifik.kmath.coroutines.collect -import scientifik.kmath.coroutines.map +import scientifik.kmath.coroutines.mapParallel import java.util.concurrent.Executors @@ -20,8 +20,8 @@ class BufferFlowTest { @Test(timeout = 2000) fun map() { runBlocking { - (1..20).asFlow().map( dispatcher) { - //println("Started $it on ${Thread.currentThread().name}") + (1..20).asFlow().mapParallel( dispatcher) { + println("Started $it on ${Thread.currentThread().name}") @Suppress("BlockingMethodInNonBlockingContext") Thread.sleep(200) it @@ -35,7 +35,7 @@ class BufferFlowTest { fun async() { runBlocking { (1..20).asFlow().async(dispatcher) { - //println("Started $it on ${Thread.currentThread().name}") + println("Started $it on ${Thread.currentThread().name}") @Suppress("BlockingMethodInNonBlockingContext") Thread.sleep(200) it diff --git a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt index e6da316cf..0896f0dcb 100644 --- a/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt +++ b/kmath-memory/src/commonMain/kotlin/scientifik/memory/MemorySpec.kt @@ -1,7 +1,5 @@ package scientifik.memory -import kotlin.reflect.KClass - /** * A specification to read or write custom objects with fixed size in bytes */ @@ -27,7 +25,7 @@ inline fun MemoryReader.readArray(spec: MemorySpec, offset: fun MemoryWriter.writeArray(spec: MemorySpec, offset: Int, array: Array) { spec.run { - for (i in 0 until array.size) { + for (i in array.indices) { write(offset + i * objectSize, array[i]) } } diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 3294469ab..59b25d340 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -1,30 +1,11 @@ plugins { id("scientifik.mpp") - id("kotlinx-atomicfu") } kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-coroutines")) - compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Scientifik.atomicfuVersion}") } } - jvmMain { - dependencies { - // https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple - //api("org.apache.commons:commons-rng-sampling:1.2") - compileOnly("org.jetbrains.kotlinx:atomicfu:${Scientifik.atomicfuVersion}") - } - } - jsMain { - dependencies { - compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Scientifik.atomicfuVersion}") - } - } - -} - -atomicfu { - variant = "VH" -} +} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt index d65b9530a..9b581afd7 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -1,6 +1,5 @@ package scientifik.kmath.prob -import kotlinx.atomicfu.atomic import scientifik.kmath.chains.Chain /** @@ -10,4 +9,7 @@ class RandomChain(val generator: RandomGenerator, private val gen: suspen override suspend fun next(): R = generator.gen() override fun fork(): Chain = RandomChain(generator.fork(), gen) -} \ No newline at end of file +} + +fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) +fun RandomGenerator.flow(gen: suspend RandomGenerator.() -> R) = chain(gen).fork() \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt new file mode 100644 index 000000000..b02eb598b --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt @@ -0,0 +1,88 @@ +package scientifik.kmath.prob + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.scanReduce +import scientifik.kmath.coroutines.mapParallel +import scientifik.kmath.operations.* +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.asIterable + +/** + * A function, that transforms a buffer of random quantities to some resulting value + */ +interface Statistic { + suspend operator fun invoke(data: Buffer): R +} + +/** + * A statistic tha could be computed separately on different blocks of data and then composed + */ +interface ComposableStatistic : Statistic { + suspend fun computeIntermediate(data: Buffer): I + suspend fun composeIntermediate(first: I, second: I): I + suspend fun toResult(intermediate: I): R + + override suspend fun invoke(data: Buffer): R = toResult(computeIntermediate(data)) +} + +@FlowPreview +@ExperimentalCoroutinesApi +fun ComposableStatistic.flowIntermediate( + flow: Flow>, + dispatcher: CoroutineDispatcher = Dispatchers.Default +): Flow = flow + .mapParallel(dispatcher) { computeIntermediate(it) } + .scanReduce(::composeIntermediate) + + +/** + * Perform a streaming statistical analysis on a chunked data. The computation of inner representation is done in parallel + * if [dispatcher] allows it. + * + * The resulting flow contains values that include the whole previous statistics, not only the last chunk. + */ +@FlowPreview +@ExperimentalCoroutinesApi +fun ComposableStatistic.flow( + flow: Flow>, + dispatcher: CoroutineDispatcher = Dispatchers.Default +): Flow = flowIntermediate(flow,dispatcher).map(::toResult) + +/** + * Arithmetic mean + */ +class Mean(val space: Space) : ComposableStatistic, T> { + override suspend fun computeIntermediate(data: Buffer): Pair = + space.run { sum(data.asIterable()) } to data.size + + override suspend fun composeIntermediate(first: Pair, second: Pair): Pair = + space.run { first.first + second.first } to (first.second + second.second) + + override suspend fun toResult(intermediate: Pair): T = + space.run { intermediate.first / intermediate.second } + + companion object { + //TODO replace with optimized version which respects overflow + val real = Mean(RealField) + val int = Mean(IntRing) + val long = Mean(LongRing) + } +} + +/** + * Non-composable median + */ +class Median(comparator: Comparator) : Statistic { + override suspend fun invoke(data: Buffer): T { + return data.asIterable().toList()[data.size / 2] //TODO check if this is correct + } + + companion object { + val real = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) + } +} \ No newline at end of file diff --git a/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt new file mode 100644 index 000000000..8b46cac52 --- /dev/null +++ b/kmath-prob/src/jvmTest/kotlin/scientifik/kmath/prob/StatisticTest.kt @@ -0,0 +1,28 @@ +package scientifik.kmath.prob + +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import scientifik.kmath.chains.flow +import scientifik.kmath.streaming.chunked +import kotlin.test.Test + +class StatisticTest { + //create a random number generator. + val generator = DefaultGenerator(1) + //Create a stateless chain from generator. + val data = generator.chain { nextDouble() } + //Convert a chaint to Flow and break it into chunks. + val chunked = data.flow().chunked(1000) + + @Test + fun testParallelMean() { + runBlocking { + val average = Mean.real + .flow(chunked) //create a flow with results + .drop(99) // Skip first 99 values and use one with total data + .first() //get 1e5 data samples average + println(average) + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index f7ef0fc42..042558d25 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,12 @@ pluginManagement { + + plugins { + id("scientifik.mpp") version "0.1.6" + id("scientifik.jvm") version "0.1.6" + id("scientifik.atomic") version "0.1.6" + id("scientifik.publish") version "0.1.6" + } + repositories { mavenLocal() jcenter() @@ -7,13 +15,10 @@ pluginManagement { maven("https://dl.bintray.com/mipt-npm/scientifik") maven("https://dl.bintray.com/kotlin/kotlinx") } + resolutionStrategy { eachPlugin { when (requested.id.id) { - "kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") - "kotlin-multiplatform" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - "kotlin2js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}") - "org.jetbrains.kotlin.frontend" -> useModule("org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.45") "scientifik.mpp", "scientifik.publish" -> useModule("scientifik:gradle-tools:${requested.version}") } } From e5e9367c436ed2c6e801d37d5552751a889affb4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 2 Sep 2019 13:07:42 +0300 Subject: [PATCH 6/8] Factorized distributions/named priors --- .../kmath/operations/ComplexDemo.kt | 11 ++++++++ .../scientifik/kmath/operations/Complex.kt | 2 ++ .../kmath/operations/OptionalOperations.kt | 4 ++- .../scientifik/kmath/prob/Distribution.kt | 4 +-- .../kmath/prob/FactorizedDistribution.kt | 26 +++++++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt diff --git a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt index 86b28303d..4841f9dd8 100644 --- a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt @@ -1,10 +1,21 @@ package scientifik.kmath.operations import scientifik.kmath.structures.NDElement +import scientifik.kmath.structures.NDField import scientifik.kmath.structures.complex fun main() { val element = NDElement.complex(2, 2) { index: IntArray -> Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) } + + + val compute = NDField.complex(8).run { + val a = produce { (it) -> i * it - it.toDouble() } + val b = 3 + val c = Complex(1.0, 1.0) + + (a pow b) + c + } + } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt index 79f9e3c35..6c529f55e 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Complex.kt @@ -56,6 +56,8 @@ object ComplexField : ExtendedFieldOperations, Field { * Complex number class */ data class Complex(val re: Double, val im: Double) : FieldElement, Comparable { + constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble()) + override fun unwrap(): Complex = this override fun Complex.wrap(): Complex = this 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 ffbbf69dd..2a77e36ef 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/OptionalOperations.kt @@ -32,6 +32,8 @@ fun >> ctg(arg: T): T = arg.conte interface PowerOperations { fun power(arg: T, pow: Number): T fun sqrt(arg: T) = power(arg, 0.5) + + infix fun T.pow(pow: Number) = power(this, pow) } infix fun >> T.pow(power: Double): T = context.power(this, power) @@ -48,7 +50,7 @@ interface ExponentialOperations { fun >> exp(arg: T): T = arg.context.exp(arg) fun >> ln(arg: T): T = arg.context.ln(arg) -interface Norm { +interface Norm { fun norm(arg: T): R } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt index 487e13876..a11799517 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -20,7 +20,7 @@ interface Distribution : Sampler { /** * Create a chain of samples from this distribution. - * The chain is not guaranteed to be stateless. + * The chain is not guaranteed to be stateless, but different sample chains should be independent. */ override fun sample(generator: RandomGenerator): Chain @@ -32,7 +32,7 @@ interface Distribution : Sampler { interface UnivariateDistribution> : Distribution { /** - * Cumulative distribution for ordered parameter + * Cumulative distribution for ordered parameter (CDF) */ fun cumulative(arg: T): Double } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt new file mode 100644 index 000000000..d70db4f83 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt @@ -0,0 +1,26 @@ +package scientifik.kmath.prob + +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain + +/** + * A multivariate distribution which takes a map of parameters + */ +interface NamedDistribution : Distribution> + +/** + * A multivariate distribution that has independent distributions for separate axis + */ +class FactorizedDistribution(val distributions: Collection>) : NamedDistribution { + + override fun probability(arg: Map): Double { + return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } + } + + override fun sample(generator: RandomGenerator): Chain> { + val chains = distributions.map { it.sample(generator) } + return SimpleChain> { + chains.fold(emptyMap()) { acc, chain -> acc + chain.next() } + } + } +} \ No newline at end of file From 1e70bebba6240410eac9a9bd3bad72749dee7ec1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 20 Nov 2019 16:12:41 +0300 Subject: [PATCH 7/8] Version update + some useful extensions --- build.gradle.kts | 2 +- examples/build.gradle.kts | 6 +- .../kmath/commons/prob/DistributionDemo.kt | 6 +- .../scientifik/kmath/structures/ComplexND.kt | 31 ++++--- gradle/artifactory.gradle | 31 ------- gradle/bintray.gradle | 85 ------------------ gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 58702 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 29 +++--- .../scientifik/kmath/operations/Algebra.kt | 2 + .../kmath/operations/NumberAlgebra.kt | 4 +- .../kmath/structures/BoxingNDField.kt | 9 ++ .../kmath/structures/ComplexNDField.kt | 9 +- .../kmath/structures/RealNDField.kt | 7 ++ .../kmath/operations/RealFieldTest.kt | 2 +- .../kotlin/scientifik/kmath/chains/Chain.kt | 18 ++-- .../scientifik/kmath/prob/Distribution.kt | 30 ++++--- .../kmath/prob/FactorizedDistribution.kt | 21 +++++ .../scientifik/kmath/prob/SamplerAlgebra.kt | 4 +- .../kotlin/scientifik/kmath/prob/Statistic.kt | 8 +- .../scientifik/kmath/prob/distributions.kt | 31 +++++++ settings.gradle.kts | 10 +-- 22 files changed, 157 insertions(+), 190 deletions(-) delete mode 100644 gradle/artifactory.gradle delete mode 100644 gradle/bintray.gradle create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/distributions.kt diff --git a/build.gradle.kts b/build.gradle.kts index 4e55432ee..0b4171486 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("scientifik.publish") version "0.1.6" apply false + id("scientifik.publish") version "0.2.5" apply false } val kmathVersion by extra("0.1.4-dev") diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 98d8607e2..ad59afc5c 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -4,8 +4,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { java kotlin("jvm") - kotlin("plugin.allopen") version "1.3.41" - id("kotlinx.benchmark") version "0.2.0-dev-2" + kotlin("plugin.allopen") version "1.3.60" + id("kotlinx.benchmark") version "0.2.0-dev-5" } configure { @@ -59,6 +59,6 @@ benchmark { tasks.withType { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = Scientifik.JVM_VERSION } } \ No newline at end of file diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt index 401dde780..3c5f53e13 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -2,17 +2,17 @@ package scientifik.kmath.commons.prob import kotlinx.coroutines.runBlocking import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.mapWithState +import scientifik.kmath.chains.collectWithState import scientifik.kmath.prob.Distribution import scientifik.kmath.prob.RandomGenerator data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) -fun Chain.mean(): Chain = mapWithState(AveragingChainState(),{it.copy()}){chain-> +fun Chain.mean(): Chain = collectWithState(AveragingChainState(),{it.copy()}){ chain-> val next = chain.next() num++ value += next - return@mapWithState value / num + return@collectWithState value / num } diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt index b57e9db79..cc8b68d85 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt @@ -2,7 +2,8 @@ package scientifik.kmath.structures import scientifik.kmath.linear.transpose import scientifik.kmath.operations.Complex -import scientifik.kmath.operations.toComplex +import scientifik.kmath.operations.ComplexField +import scientifik.kmath.operations.invoke import kotlin.system.measureTimeMillis fun main() { @@ -39,19 +40,21 @@ fun main() { fun complexExample() { //Create a context for 2-d structure with complex values - NDField.complex(4, 8).run { - //a constant real-valued structure - val x = one * 2.5 - operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) - //a structure generator specific to this context - val matrix = produce { (k, l) -> - k + l*i + ComplexField { + nd(4, 8) { + //a constant real-valued structure + val x = one * 2.5 + operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) + //a structure generator specific to this context + val matrix = produce { (k, l) -> + k + l * i + } + + //Perform sum + val sum = matrix + x + 1.0 + + //Represent the sum as 2d-structure and transpose + sum.as2D().transpose() } - - //Perform sum - val sum = matrix + x + 1.0 - - //Represent the sum as 2d-structure and transpose - sum.as2D().transpose() } } diff --git a/gradle/artifactory.gradle b/gradle/artifactory.gradle deleted file mode 100644 index 12e59642b..000000000 --- a/gradle/artifactory.gradle +++ /dev/null @@ -1,31 +0,0 @@ -apply plugin: "com.jfrog.artifactory" - -artifactory { - def artifactory_user = project.hasProperty('artifactoryUser') ? project.property('artifactoryUser') : "" - def artifactory_password = project.hasProperty('artifactoryPassword') ? project.property('artifactoryPassword') : "" - def artifactory_contextUrl = 'http://npm.mipt.ru:8081/artifactory' - - contextUrl = artifactory_contextUrl //The base Artifactory URL if not overridden by the publisher/resolver - publish { - repository { - repoKey = 'gradle-dev-local' - username = artifactory_user - password = artifactory_password - } - - defaults { - publications('jvm', 'js', 'kotlinMultiplatform', 'metadata') - publishBuildInfo = false - publishArtifacts = true - publishPom = true - publishIvy = false - } - } - resolve { - repository { - repoKey = 'gradle-dev' - username = artifactory_user - password = artifactory_password - } - } -} \ No newline at end of file diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle deleted file mode 100644 index 8da83c860..000000000 --- a/gradle/bintray.gradle +++ /dev/null @@ -1,85 +0,0 @@ -apply plugin: 'com.jfrog.bintray' - -def vcs = "https://github.com/mipt-npm/kmath" - -def pomConfig = { - licenses { - license { - name "The Apache Software License, Version 2.0" - url "http://www.apache.org/licenses/LICENSE-2.0.txt" - distribution "repo" - } - } - developers { - developer { - id "MIPT-NPM" - name "MIPT nuclear physics methods laboratory" - organization "MIPT" - organizationUrl "http://npm.mipt.ru" - } - } - scm { - url vcs - } -} - -project.ext.configureMavenCentralMetadata = { pom -> - def root = asNode() - root.appendNode('name', project.name) - root.appendNode('description', project.description) - root.appendNode('url', vcs) - root.children().last() + pomConfig -} - -project.ext.configurePom = pomConfig - - -// Configure publishing -publishing { - repositories { - maven { - url = "https://bintray.com/mipt-npm/scientifik" - } - } - - // Process each publication we have in this project - publications.all { publication -> - // apply changes to pom.xml files, see pom.gradle - pom.withXml(configureMavenCentralMetadata) - - - } -} - -bintray { - user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') - key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') - publish = true - override = true // for multi-platform Kotlin/Native publishing - - pkg { - userOrg = "mipt-npm" - repo = "scientifik" - name = "scientifik.kmath" - issueTrackerUrl = "https://github.com/mipt-npm/kmath/issues" - licenses = ['Apache-2.0'] - vcsUrl = vcs - version { - name = project.version - vcsTag = project.version - released = new Date() - } - } -} - -bintrayUpload.dependsOn publishToMavenLocal - -// This is for easier debugging of bintray uploading problems -bintrayUpload.doFirst { - publications = project.publishing.publications.findAll { - !it.name.contains('-test') && it.name != 'kotlinMultiplatform' - }.collect { - println("Uploading artifact '$it.groupId:$it.artifactId:$it.version' from publication '$it.name'") - it.name//https://github.com/bintray/gradle-bintray-plugin/issues/256 - } -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b 100644 GIT binary patch delta 16535 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 T.invoke(block: T.() -> R): R = run(block) + /** * Space-like operations without neutral element */ diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt index a8c7f99fa..9639e4c28 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/NumberAlgebra.kt @@ -1,7 +1,7 @@ package scientifik.kmath.operations import kotlin.math.abs -import kotlin.math.pow +import kotlin.math.pow as kpow /** * Advanced Number-like field that implements basic operations @@ -45,7 +45,7 @@ object RealField : ExtendedField, Norm { override inline fun sin(arg: Double) = kotlin.math.sin(arg) override inline fun cos(arg: Double) = kotlin.math.cos(arg) - override inline fun power(arg: Double, pow: Number) = arg.pow(pow.toDouble()) + override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble()) override inline fun exp(arg: Double) = kotlin.math.exp(arg) override inline fun ln(arg: Double) = kotlin.math.ln(arg) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt index a73c58939..e6d4b226d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDField.kt @@ -70,4 +70,13 @@ class BoxingNDField>( override fun NDBuffer.toElement(): FieldElement, *, out BufferedNDField> = BufferedNDFieldElement(this@BoxingNDField, buffer) +} + +inline fun , R> F.nd( + noinline bufferFactory: BufferFactory, + vararg shape: Int, + action: NDField.() -> R +): R { + val ndfield: BoxingNDField = NDField.boxing(this, *shape, bufferFactory = bufferFactory) + return ndfield.action() } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index e9d518bd5..a79366a99 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -134,4 +134,11 @@ operator fun ComplexNDElement.minus(arg: Double) = fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement = - NDField.complex(*shape).produce(initializer) \ No newline at end of file + NDField.complex(*shape).produce(initializer) + +/** + * Produce a context for n-dimensional operations inside this real field + */ +inline fun ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R { + return NDField.complex(*shape).run(action) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt index 82a237817..8c1bd4239 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/RealNDField.kt @@ -119,3 +119,10 @@ operator fun RealNDElement.plus(arg: Double) = */ operator fun RealNDElement.minus(arg: Double) = map { it - arg } + +/** + * Produce a context for n-dimensional operations inside this real field + */ +inline fun RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R { + return NDField.real(*shape).run(action) +} \ No newline at end of file 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 aedbf6655..779cfc4b8 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/operations/RealFieldTest.kt @@ -6,7 +6,7 @@ import kotlin.test.assertEquals class RealFieldTest { @Test fun testSqrt() { - val sqrt = with(RealField) { + val sqrt = RealField { sqrt(25 * one) } assertEquals(5.0, sqrt) diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 161dc3b7f..8dba9e215 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -122,23 +122,23 @@ class ConstantChain(val value: T) : Chain { * Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed * since mapped chain consumes tokens. Accepts regular transformation function */ -fun Chain.pipe(func: suspend (T) -> R): Chain = object : Chain { - override suspend fun next(): R = func(this@pipe.next()) - override fun fork(): Chain = this@pipe.fork().pipe(func) +fun Chain.map(func: suspend (T) -> R): Chain = object : Chain { + override suspend fun next(): R = func(this@map.next()) + override fun fork(): Chain = this@map.fork().map(func) } /** * Map the whole chain */ -fun Chain.map(mapper: suspend (Chain) -> R): Chain = object : Chain { - override suspend fun next(): R = mapper(this@map) - override fun fork(): Chain = this@map.fork().map(mapper) +fun Chain.collect(mapper: suspend (Chain) -> R): Chain = object : Chain { + override suspend fun next(): R = mapper(this@collect) + override fun fork(): Chain = this@collect.fork().collect(mapper) } -fun Chain.mapWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = +fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = object : Chain { - override suspend fun next(): R = state.mapper(this@mapWithState) - override fun fork(): Chain = this@mapWithState.fork().mapWithState(stateFork(state), stateFork, mapper) + override suspend fun next(): R = state.mapper(this@collectWithState) + override fun fork(): Chain = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) } /** diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt index a11799517..44af84d5d 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -1,8 +1,9 @@ package scientifik.kmath.prob import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.map -import kotlin.jvm.JvmName +import scientifik.kmath.chains.collect +import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.BufferFactory interface Sampler { fun sample(generator: RandomGenerator): Chain @@ -45,24 +46,27 @@ fun > UnivariateDistribution.integral(from: T, to: T): Doub return cumulative(to) - cumulative(from) } - /** * Sample a bunch of values */ -fun Sampler.sampleBunch(generator: RandomGenerator, size: Int): Chain> { +fun Sampler.sampleBuffer( + generator: RandomGenerator, + size: Int, + bufferFactory: BufferFactory = Buffer.Companion::boxing +): Chain> { require(size > 1) - return sample(generator).map{chain -> - List(size){chain.next()} + //creating temporary storage once + val tmp = ArrayList(size) + return sample(generator).collect { chain -> + for (i in tmp.indices) { + tmp[i] = chain.next() + } + bufferFactory(size) { tmp[it] } } } /** * Generate a bunch of samples from real distributions */ -@JvmName("realSampleBunch") -fun Sampler.sampleBunch(generator: RandomGenerator, size: Int): Chain { - require(size > 1) - return sample(generator).map{chain -> - DoubleArray(size){chain.next()} - } -} \ No newline at end of file +fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int) = + sampleBuffer(generator, size, Buffer.Companion::real) \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt index d70db4f83..ea526c058 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt @@ -23,4 +23,25 @@ class FactorizedDistribution(val distributions: Collection acc + chain.next() } } } +} + +class NamedDistributionWrapper(val name: String, val distribution: Distribution) : NamedDistribution { + override fun probability(arg: Map): Double = distribution.probability( + arg[name] ?: error("Argument with name $name not found in input parameters") + ) + + override fun sample(generator: RandomGenerator): Chain> { + val chain = distribution.sample(generator) + return SimpleChain { + mapOf(name to chain.next()) + } + } +} + +class DistributionBuilder{ + private val distributions = ArrayList>() + + infix fun String.to(distribution: Distribution){ + distributions.add(NamedDistributionWrapper(this,distribution)) + } } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt index ca9d74aab..3a60c0bda 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt @@ -2,7 +2,7 @@ package scientifik.kmath.prob import scientifik.kmath.chains.Chain import scientifik.kmath.chains.ConstantChain -import scientifik.kmath.chains.pipe +import scientifik.kmath.chains.map import scientifik.kmath.chains.zip import scientifik.kmath.operations.Space @@ -26,6 +26,6 @@ class SamplerSpace(val space: Space) : Space> { } override fun multiply(a: Sampler, k: Number): Sampler = BasicSampler { generator -> - a.sample(generator).pipe { space.run { it * k.toDouble() } } + a.sample(generator).map { space.run { it * k.toDouble() } } } } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt index b02eb598b..1af2570b0 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Statistic.kt @@ -21,10 +21,16 @@ interface Statistic { /** * A statistic tha could be computed separately on different blocks of data and then composed + * @param T - source type + * @param I - intermediate block type + * @param R - result type */ interface ComposableStatistic : Statistic { + //compute statistic on a single block suspend fun computeIntermediate(data: Buffer): I + //Compose two blocks suspend fun composeIntermediate(first: I, second: I): I + //Transform block to result suspend fun toResult(intermediate: I): R override suspend fun invoke(data: Buffer): R = toResult(computeIntermediate(data)) @@ -32,7 +38,7 @@ interface ComposableStatistic : Statistic { @FlowPreview @ExperimentalCoroutinesApi -fun ComposableStatistic.flowIntermediate( +private fun ComposableStatistic.flowIntermediate( flow: Flow>, dispatcher: CoroutineDispatcher = Dispatchers.Default ): Flow = flow diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/distributions.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/distributions.kt new file mode 100644 index 000000000..ff6e2a551 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/distributions.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.prob + +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain + +class UniformDistribution(val range: ClosedFloatingPointRange) : UnivariateDistribution { + + private val length = range.endInclusive - range.start + + override fun probability(arg: Double): Double { + return if (arg in range) { + return 1.0 / length + } else { + 0.0 + } + } + + override fun sample(generator: RandomGenerator): Chain { + return SimpleChain { + range.start + generator.nextDouble() * length + } + } + + override fun cumulative(arg: Double): Double { + return when { + arg < range.start -> 0.0 + arg >= range.endInclusive -> 1.0 + else -> (arg - range.start) / length + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 042558d25..f52edfac5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,10 +1,10 @@ pluginManagement { plugins { - id("scientifik.mpp") version "0.1.6" - id("scientifik.jvm") version "0.1.6" - id("scientifik.atomic") version "0.1.6" - id("scientifik.publish") version "0.1.6" + id("scientifik.mpp") version "0.2.5" + id("scientifik.jvm") version "0.2.5" + id("scientifik.atomic") version "0.2.5" + id("scientifik.publish") version "0.2.5" } repositories { @@ -25,8 +25,6 @@ pluginManagement { } } -enableFeaturePreview("GRADLE_METADATA") - rootProject.name = "kmath" include( ":kmath-memory", From bf2f4db468afe8b619c392c5745f9ed4a7e750dd Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 8 Dec 2019 10:59:49 +0300 Subject: [PATCH 8/8] readme update --- .github/workflows/gradle.yml | 17 ++++ README.md | 79 ++++++++----------- build.gradle.kts | 4 +- .../scientifik/kmath/operations/Algebra.kt | 3 + 4 files changed, 53 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 000000000..adc74adfe --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,17 @@ +name: Gradle build + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Build with Gradle + run: ./gradlew build diff --git a/README.md b/README.md index a1050f6ff..5678fc530 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/scientifik/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) +Bintray-dev: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) + [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) # KMath Could be pronounced as `key-math`. The Kotlin MATHematics library is intended as a Kotlin-based analog to Python's `numpy` library. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. +# Goal +* Provide a flexible and powerful API to work with mathematics abstractions in Kotlin-multiplatform (JVM and JS for now and Native in future). +* Provide basic multiplatform implementations for those abstractions (without significant performance optimization). +* Provide bindings and wrappers with those abstractions for popular optimized platform libraries. + +## Non-goals +* Be like Numpy. It was the idea at the beginning, but we decided that we can do better in terms of API. +* Provide best performance out of the box. We have specialized libraries for that. Need only API wrappers for them. +* Cover all cases as immediately and in one bundle. We will modularize everything and add new features gradually. +* Provide specialized behavior in the core. API is made generic on purpose, so one needs to specialize for types, like for `Double` in the core. For that we will have specialization modules like `for-real`, which will give better experience for those, who want to work with specific types. + ## Features Actual feature list is [here](doc/features.md) @@ -48,58 +61,15 @@ The plan is to have wrappers for koma implementations for compatibility with kma ## Multi-platform support -KMath is developed as a multi-platform library, which means that most of interfaces are declared in the [common module](kmath-core/src/commonMain). -Implementation is also done in the common module wherever possible. In some cases, features are delegated to -platform-specific implementations even if they could be done in the common module for performance reasons. -Currently, the JVM is the main focus of development, however Kotlin/Native and Kotlin/JS contributions are also welcome. +KMath is developed as a multi-platform library, which means that most of interfaces are declared in the [common module](kmath-core/src/commonMain). Implementation is also done in the common module wherever possible. In some cases, features are delegated to platform-specific implementations even if they could be done in the common module for performance reasons. Currently, the JVM is the main focus of development, however Kotlin/Native and Kotlin/JS contributions are also welcome. ## Performance -Calculation performance is one of major goals of KMath in the future, but in some cases it is not possible to achieve -both performance and flexibility. We expect to focus on creating convenient universal API first and then work on -increasing performance for specific cases. We expect the worst KMath benchmarks will perform better than native Python, -but worse than optimized native/SciPy (mostly due to boxing operations on primitive numbers). The best performance -of optimized parts should be better than SciPy. +Calculation performance is one of major goals of KMath in the future, but in some cases it is not possible to achieve both performance and flexibility. We expect to focus on creating convenient universal API first and then work on increasing performance for specific cases. We expect the worst KMath benchmarks will perform better than native Python, but worse than optimized native/SciPy (mostly due to boxing operations on primitive numbers). The best performance of optimized parts could be better than SciPy. -## Releases +### Dependency -Working builds can be obtained here: [![](https://jitpack.io/v/altavir/kmath.svg)](https://jitpack.io/#altavir/kmath). - -### Development - -The project is currently in pre-release stage. Nightly builds can be used by adding an additional repository to the Gradle config like so: - -```groovy -repositories { - maven { url = "http://npm.mipt.ru:8081/artifactory/gradle-dev" } - mavenCentral() -} -``` - -or for the Gradle Kotlin DSL: - -```kotlin -repositories { - maven("http://npm.mipt.ru:8081/artifactory/gradle-dev") - mavenCentral() -} -``` - -Then use a regular dependency like so: - -```groovy -api "scientifik:kmath-core-jvm:0.1.0-dev" -``` - -or in the Gradle Kotlin DSL: - -```kotlin -api("scientifik:kmath-core-jvm:0.1.0-dev") -``` - -### Release - -Release artifacts are accessible from bintray with following configuration: +Release artifacts are accessible from bintray with following configuration (see documentation for [kotlin-multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) form more details): ```kotlin repositories{ @@ -107,10 +77,23 @@ repositories{ } dependencies{ - api("scientifik:kmath-core-jvm:0.1.0") + api("scientifik:kmath-core:${kmathVersion}") + //api("scientifik:kmath-core-jvm:${kmathVersion}") for jvm-specific version } ``` +Gradle `5.2+` is required for multiplatform artifacts. + +### Development + +Development builds are accessible from the reposirtory +```kotlin +repositories{ + maven("https://dl.bintray.com/mipt-npm/dev") +} +``` +with the same artifact names. + ## Contributing The project requires a lot of additional work. Please feel free to contribute in any way and propose new features. diff --git a/build.gradle.kts b/build.gradle.kts index 0b4171486..a63966dfe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ plugins { - id("scientifik.publish") version "0.2.5" apply false + id("scientifik.publish") version "0.2.6" apply false } -val kmathVersion by extra("0.1.4-dev") +val kmathVersion by extra("0.1.4-dev-1") val bintrayRepo by extra("scientifik") val githubProject by extra("kmath") diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt index 9186e891f..0ed769db8 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/operations/Algebra.kt @@ -1,5 +1,8 @@ package scientifik.kmath.operations +@DslMarker +annotation class KMathContext + /** * Marker interface for any algebra */