Merge branch 'dev' into dimensions

# Conflicts:
#	build.gradle.kts
#	buildSrc/build.gradle.kts
#	settings.gradle.kts
This commit is contained in:
Alexander Nozik 2019-12-09 17:40:58 +03:00
commit a156feb397
69 changed files with 815 additions and 898 deletions

17
.github/workflows/gradle.yml vendored Normal file
View File

@ -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

View File

@ -1,9 +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: [ ![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 # KMath
Could be pronounced as `key-math`. 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. 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 ## Features
Actual feature list is [here](doc/features.md) Actual feature list is [here](doc/features.md)
@ -46,58 +61,15 @@ The plan is to have wrappers for koma implementations for compatibility with kma
## Multi-platform support ## 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). 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.
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 ## Performance
Calculation performance is one of major goals of KMath in the future, but in some cases it is not possible to achieve 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.
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.
## Releases ### Dependency
Working builds can be obtained here: [![](https://jitpack.io/v/altavir/kmath.svg)](https://jitpack.io/#altavir/kmath). Release artifacts are accessible from bintray with following configuration (see documentation for [kotlin-multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) form more details):
### 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:
```kotlin ```kotlin
repositories{ repositories{
@ -105,10 +77,23 @@ repositories{
} }
dependencies{ 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 ## Contributing
The project requires a lot of additional work. Please feel free to contribute in any way and propose new features. The project requires a lot of additional work. Please feel free to contribute in any way and propose new features.

View File

@ -1,9 +1,16 @@
val kmathVersion by extra("0.1.3-dev-2") plugins {
id("scientifik.publish") version "0.2.6" apply false
}
val kmathVersion by extra("0.1.4-dev-1")
val bintrayRepo by extra("scientifik")
val githubProject by extra("kmath")
allprojects { allprojects {
repositories { repositories {
jcenter() jcenter()
maven("https://kotlin.bintray.com/kotlinx") maven("https://dl.bintray.com/kotlin/kotlinx")
} }
group = "scientifik" group = "scientifik"
@ -11,8 +18,7 @@ allprojects {
} }
subprojects { subprojects {
apply(plugin = "dokka-publish")
if (name.startsWith("kmath")) { if (name.startsWith("kmath")) {
apply(plugin = "npm-publish") apply(plugin = "scientifik.publish")
} }
} }

View File

@ -1,20 +0,0 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
jcenter()
}
val kotlinVersion = "1.3.40"
// 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")
}

View File

@ -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"
}

View File

@ -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<KotlinMultiplatformExtension>()?.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<PublishingExtension> {
targets.all {
val publication = publications.findByName(name) as MavenPublication
// Patch publications with fake javadoc
publication.artifact(kdocJar.get())
}
}
}
extensions.findByType<KotlinJvmProjectExtension>()?.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<PublishingExtension> {
publications.filterIsInstance<MavenPublication>().forEach { publication ->
publication.artifact(kdocJar.get())
}
}
}
}

View File

@ -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)

View File

@ -1,82 +0,0 @@
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")
}
}

View File

@ -1,136 +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<MavenPublication>().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)
}
}
}
//workaround for bintray
tasks.withType<BintrayUploadTask> {
doFirst {
publishing.publications
.filterIsInstance<MavenPublication>()
.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
val artifactoryContextUrl = "http://npm.mipt.ru:8081/artifactory"
setContextUrl(artifactoryContextUrl)//The base Artifactory URL if not overridden by the publisher/resolver
publish(delegateClosureOf<PublisherConfig> {
repository(delegateClosureOf<GroovyObject> {
setProperty("repoKey", "gradle-dev-local")
setProperty("username", artifactoryUser)
setProperty("password", artifactoryPassword)
})
defaults(delegateClosureOf<GroovyObject> {
invokeMethod("publications", arrayOf("jvm", "js", "kotlinMultiplatform", "metadata"))
})
})
resolve(delegateClosureOf<ResolverConfig> {
repository(delegateClosureOf<GroovyObject> {
setProperty("repoKey", "gradle-dev")
setProperty("username", artifactoryUser)
setProperty("password", artifactoryPassword)
})
})
}

View File

@ -1,12 +1,11 @@
import org.jetbrains.gradle.benchmarks.JvmBenchmarkTarget
import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
java java
kotlin("jvm") kotlin("jvm")
kotlin("plugin.allopen") version "1.3.31" kotlin("plugin.allopen") version "1.3.61"
id("org.jetbrains.gradle.benchmarks.plugin") version "0.1.7-dev-24" id("kotlinx.benchmark") version "0.2.0-dev-5"
} }
configure<AllOpenExtension> { configure<AllOpenExtension> {
@ -17,6 +16,7 @@ repositories {
maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlin-eap")
maven("http://dl.bintray.com/kyonifer/maven") maven("http://dl.bintray.com/kyonifer/maven")
maven ("https://dl.bintray.com/orangy/maven") maven ("https://dl.bintray.com/orangy/maven")
mavenCentral() mavenCentral()
} }
@ -29,10 +29,11 @@ dependencies {
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))
implementation(project(":kmath-koma")) implementation(project(":kmath-koma"))
implementation(project(":kmath-viktor"))
implementation("com.kyonifer:koma-core-ejml:0.12") 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) "benchmarksCompile"(sourceSets.main.get().compileClasspath)
@ -43,10 +44,7 @@ benchmark {
// Setup configurations // Setup configurations
targets { targets {
// This one matches sourceSet name above // This one matches sourceSet name above
register("benchmarks") { register("benchmarks")
this as JvmBenchmarkTarget
jmhVersion = "1.21"
}
} }
configurations { configurations {
@ -62,6 +60,6 @@ benchmark {
tasks.withType<KotlinCompile> { tasks.withType<KotlinCompile> {
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = Scientifik.JVM_VERSION
} }
} }

View File

@ -2,17 +2,17 @@ package scientifik.kmath.commons.prob
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import scientifik.kmath.chains.Chain import scientifik.kmath.chains.Chain
import scientifik.kmath.chains.mapWithState import scientifik.kmath.chains.collectWithState
import scientifik.kmath.prob.Distribution import scientifik.kmath.prob.Distribution
import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.RandomGenerator
data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) data class AveragingChainState(var num: Int = 0, var value: Double = 0.0)
fun Chain<Double>.mean(): Chain<Double> = mapWithState(AveragingChainState(),{it.copy()}){chain-> fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(),{it.copy()}){ chain->
val next = chain.next() val next = chain.next()
num++ num++
value += next value += next
return@mapWithState value / num return@collectWithState value / num
} }

View File

@ -1,10 +1,21 @@
package scientifik.kmath.operations package scientifik.kmath.operations
import scientifik.kmath.structures.NDElement import scientifik.kmath.structures.NDElement
import scientifik.kmath.structures.NDField
import scientifik.kmath.structures.complex import scientifik.kmath.structures.complex
fun main() { fun main() {
val element = NDElement.complex(2, 2) { index: IntArray -> val element = NDElement.complex(2, 2) { index: IntArray ->
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) 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
}
} }

View File

@ -1,5 +1,9 @@
package scientifik.kmath.structures package scientifik.kmath.structures
import scientifik.kmath.linear.transpose
import scientifik.kmath.operations.Complex
import scientifik.kmath.operations.ComplexField
import scientifik.kmath.operations.invoke
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
fun main() { fun main() {
@ -32,3 +36,25 @@ fun main() {
println("Complex addition completed in $complexTime millis") println("Complex addition completed in $complexTime millis")
} }
fun complexExample() {
//Create a context for 2-d structure with complex values
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()
}
}
}

View File

@ -4,7 +4,13 @@ import kotlinx.coroutines.GlobalScope
import scientifik.kmath.operations.RealField import scientifik.kmath.operations.RealField
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
fun main(args: Array<String>) { internal inline fun measureAndPrint(title: String, block: () -> Unit) {
val time = measureTimeMillis(block)
println("$title completed in $time millis")
}
fun main() {
val dim = 1000 val dim = 1000
val n = 1000 val n = 1000
@ -15,8 +21,7 @@ fun main(args: Array<String>) {
//A generic boxing field. It should be used for objects, not primitives. //A generic boxing field. It should be used for objects, not primitives.
val genericField = NDField.boxing(RealField, dim, dim) val genericField = NDField.boxing(RealField, dim, dim)
measureAndPrint("Automatic field addition") {
val autoTime = measureTimeMillis {
autoField.run { autoField.run {
var res = one var res = one
repeat(n) { repeat(n) {
@ -25,18 +30,14 @@ fun main(args: Array<String>) {
} }
} }
println("Automatic field addition completed in $autoTime millis") measureAndPrint("Element addition"){
val elementTime = measureTimeMillis {
var res = genericField.one var res = genericField.one
repeat(n) { repeat(n) {
res += 1.0 res += 1.0
} }
} }
println("Element addition completed in $elementTime millis") measureAndPrint("Specialized addition") {
val specializedTime = measureTimeMillis {
specializedField.run { specializedField.run {
var res: NDBuffer<Double> = one var res: NDBuffer<Double> = one
repeat(n) { repeat(n) {
@ -45,10 +46,7 @@ fun main(args: Array<String>) {
} }
} }
println("Specialized addition completed in $specializedTime millis") measureAndPrint("Lazy addition") {
val lazyTime = measureTimeMillis {
val res = specializedField.one.mapAsync(GlobalScope) { val res = specializedField.one.mapAsync(GlobalScope) {
var c = 0.0 var c = 0.0
repeat(n) { repeat(n) {
@ -60,9 +58,7 @@ fun main(args: Array<String>) {
res.elements().forEach { it.second } res.elements().forEach { it.second }
} }
println("Lazy addition completed in $lazyTime millis") measureAndPrint("Generic addition") {
val genericTime = measureTimeMillis {
//genericField.run(action) //genericField.run(action)
genericField.run { genericField.run {
var res: NDBuffer<Double> = one var res: NDBuffer<Double> = one
@ -72,6 +68,4 @@ fun main(args: Array<String>) {
} }
} }
println("Generic addition completed in $genericTime millis")
} }

View File

@ -0,0 +1,76 @@
package scientifik.kmath.structures
import org.jetbrains.bio.viktor.F64Array
import scientifik.kmath.operations.RealField
import scientifik.kmath.viktor.ViktorNDField
fun main() {
val dim = 1000
val n = 400
// automatically build context most suited for given type.
val autoField = NDField.auto(RealField, dim, dim)
val realField = NDField.real(dim,dim)
val viktorField = ViktorNDField(intArrayOf(dim, dim))
autoField.run {
var res = one
repeat(n/2) {
res += 1.0
}
}
measureAndPrint("Automatic field addition") {
autoField.run {
var res = one
repeat(n) {
res += 1.0
}
}
}
viktorField.run {
var res = one
repeat(n/2) {
res += one
}
}
measureAndPrint("Viktor field addition") {
viktorField.run {
var res = one
repeat(n) {
res += one
}
}
}
measureAndPrint("Raw Viktor") {
val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim))
var res = one
repeat(n) {
res = res + one
}
}
measureAndPrint("Automatic field log") {
realField.run {
val fortyTwo = produce { 42.0 }
var res = one
repeat(n) {
res = ln(fortyTwo)
}
}
}
measureAndPrint("Raw Viktor log") {
val fortyTwo = F64Array.full(dim, dim, init = 42.0)
var res: F64Array
repeat(n) {
res = fortyTwo.log()
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

35
gradlew vendored
View File

@ -7,7 +7,7 @@
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # 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 # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
@ -125,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@ -154,19 +154,19 @@ if $cygwin ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

2
gradlew.bat vendored
View File

@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with the License. @rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem http://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS, @rem distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,6 +1,5 @@
plugins { plugins {
kotlin("jvm") id("scientifik.jvm")
`maven-publish`
} }
description = "Commons math binding for kmath" description = "Commons math binding for kmath"
@ -10,21 +9,4 @@ dependencies {
api(project(":kmath-coroutines")) api(project(":kmath-coroutines"))
api(project(":kmath-prob")) api(project(":kmath-prob"))
api("org.apache.commons:commons-math3:3.6.1") 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())
}
}
} }

View File

@ -11,7 +11,7 @@ import scientifik.kmath.structures.*
/** /**
* * Streaming and buffer transformations
*/ */
object Transformations { object Transformations {

View File

@ -1,5 +1,5 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
} }
kotlin.sourceSets { kotlin.sourceSets {
@ -8,6 +8,4 @@ kotlin.sourceSets {
api(project(":kmath-memory")) api(project(":kmath-memory"))
} }
} }
//mingwMain {}
//mingwTest {}
} }

View File

@ -183,7 +183,7 @@ fun <T : Any> LUPDecomposition<T>.solve(type: KClass<T>, matrix: Matrix<T>): Mat
elementContext.run { elementContext.run {
// Apply permutations to b // Apply permutations to b
val bp = create { i, j -> zero } val bp = create { _, _ -> zero }
for (row in 0 until pivot.size) { for (row in 0 until pivot.size) {
val bpRow = bp.row(row) val bpRow = bp.row(row)

View File

@ -3,10 +3,12 @@ package scientifik.kmath.linear
import scientifik.kmath.operations.Field import scientifik.kmath.operations.Field
import scientifik.kmath.operations.Norm import scientifik.kmath.operations.Norm
import scientifik.kmath.operations.RealField import scientifik.kmath.operations.RealField
import scientifik.kmath.structures.Buffer
import scientifik.kmath.structures.Matrix import scientifik.kmath.structures.Matrix
import scientifik.kmath.structures.VirtualBuffer import scientifik.kmath.structures.VirtualBuffer
import scientifik.kmath.structures.asSequence import scientifik.kmath.structures.asSequence
typealias Point<T> = Buffer<T>
/** /**
* A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors * 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<T : Any> {
fun inverse(a: Matrix<T>): Matrix<T> fun inverse(a: Matrix<T>): Matrix<T>
} }
/**
* Convert vector to array (copying content of array)
*/
fun <T : Any> Array<T>.toVector(field: Field<T>) = Vector.generic(size, field) { this[it] }
fun DoubleArray.toVector() = Vector.real(this.size) { this[it] }
fun List<Double>.toVector() = Vector.real(this.size) { this[it] }
object VectorL2Norm : Norm<Point<out Number>, Double> {
override fun norm(arg: Point<out Number>): Double =
kotlin.math.sqrt(arg.asSequence().sumByDouble { it.toDouble() })
}
typealias RealVector = Vector<Double, RealField>
typealias RealMatrix = Matrix<Double> typealias RealMatrix = Matrix<Double>
/** /**

View File

@ -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<Double>.asVector() = RealVector(this.asBuffer())
object VectorL2Norm : Norm<Point<out Number>, Double> {
override fun norm(arg: Point<out Number>): Double =
kotlin.math.sqrt(arg.asSequence().sumByDouble { it.toDouble() })
}
inline class RealVector(val point: Point<Double>) :
SpaceElement<Point<Double>, RealVector, VectorSpace<Double, RealField>>, Point<Double> {
override val context: VectorSpace<Double, RealField> get() = space(point.size)
override fun unwrap(): Point<Double> = point
override fun Point<Double>.wrap(): RealVector = RealVector(this)
override val size: Int get() = point.size
override fun get(index: Int): Double = point[index]
override fun iterator(): Iterator<Double> = point.iterator()
companion object {
private val spaceCache = HashMap<Int, BufferVectorSpace<Double, RealField>>()
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) }
}
}
}

View File

@ -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<T> = Buffer<T>
fun <T : Any, S : Space<T>> BufferVectorSpace<T, S>.produceElement(initializer: (Int) -> T): Vector<T, S> =
BufferVector(this, produce(initializer))
@JvmName("produceRealElement")
fun BufferVectorSpace<Double, RealField>.produceElement(initializer: (Int) -> Double): Vector<Double, RealField> =
BufferVector(this, produce(initializer))
/**
* A point coupled to the linear space
*/
@Deprecated("Use VectorContext instead")
interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, Vector<T, S>, VectorSpace<T, S>>, Point<T> {
override val size: Int get() = context.size
override operator fun plus(b: Point<T>): Vector<T, S> = context.add(this, b).wrap()
override operator fun minus(b: Point<T>): Vector<T, S> = context.add(this, context.multiply(b, -1.0)).wrap()
override operator fun times(k: Number): Vector<T, S> = context.multiply(this, k.toDouble()).wrap()
override operator fun div(k: Number): Vector<T, S> = context.multiply(this, 1.0 / k.toDouble()).wrap()
companion object {
/**
* Create vector with custom field
*/
fun <T : Any, S : Space<T>> generic(size: Int, field: S, initializer: (Int) -> T): Vector<T, S> =
VectorSpace.buffered(size, field).produceElement(initializer)
fun real(size: Int, initializer: (Int) -> Double): Vector<Double, RealField> =
VectorSpace.real(size).produceElement(initializer)
fun ofReal(vararg elements: Double): Vector<Double, RealField> =
VectorSpace.real(elements.size).produceElement { elements[it] }
}
}
@Deprecated("Use VectorContext instead")
data class BufferVector<T : Any, S : Space<T>>(override val context: VectorSpace<T, S>, val buffer: Buffer<T>) :
Vector<T, S> {
init {
if (context.size != buffer.size) {
error("Array dimension mismatch")
}
}
override fun get(index: Int): T {
return buffer[index]
}
override fun unwrap(): Point<T> = this
override fun Point<T>.wrap(): Vector<T, S> = BufferVector(context, this)
override fun iterator(): Iterator<T> = (0 until size).map { buffer[it] }.iterator()
override fun toString(): String =
this.asSequence().joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
}

View File

@ -10,29 +10,32 @@ import kotlin.jvm.JvmName
* @param R type of resulting iterable * @param R type of resulting iterable
* @param initial lazy evaluated * @param initial lazy evaluated
*/ */
fun <T, R> Iterator<T>.cumulative(initial: R, operation: (T, R) -> R): Iterator<R> = object : Iterator<R> { fun <T, R> Iterator<T>.cumulative(initial: R, operation: (R, T) -> R): Iterator<R> = object : Iterator<R> {
var state: R = initial var state: R = initial
override fun hasNext(): Boolean = this@cumulative.hasNext() override fun hasNext(): Boolean = this@cumulative.hasNext()
override fun next(): R { override fun next(): R {
state = operation.invoke(this@cumulative.next(), state) state = operation(state, this@cumulative.next())
return state return state
} }
} }
fun <T, R> Iterable<T>.cumulative(initial: R, operation: (T, R) -> R): Iterable<R> = object : Iterable<R> { fun <T, R> Iterable<T>.cumulative(initial: R, operation: (R, T) -> R): Iterable<R> = object : Iterable<R> {
override fun iterator(): Iterator<R> = this@cumulative.iterator().cumulative(initial, operation) override fun iterator(): Iterator<R> = this@cumulative.iterator().cumulative(initial, operation)
} }
fun <T, R> Sequence<T>.cumulative(initial: R, operation: (T, R) -> R): Sequence<R> = object : Sequence<R> { fun <T, R> Sequence<T>.cumulative(initial: R, operation: (R, T) -> R): Sequence<R> = object : Sequence<R> {
override fun iterator(): Iterator<R> = this@cumulative.iterator().cumulative(initial, operation) override fun iterator(): Iterator<R> = this@cumulative.iterator().cumulative(initial, operation)
} }
fun <T, R> List<T>.cumulative(initial: R, operation: (T, R) -> R): List<R> = fun <T, R> List<T>.cumulative(initial: R, operation: (R, T) -> R): List<R> =
this.iterator().cumulative(initial, operation).asSequence().toList() this.iterator().cumulative(initial, operation).asSequence().toList()
//Cumulative sum //Cumulative sum
/**
* Cumulative sum with custom space
*/
fun <T> Iterable<T>.cumulativeSum(space: Space<T>) = with(space) { fun <T> Iterable<T>.cumulativeSum(space: Space<T>) = with(space) {
cumulative(zero) { element: T, sum: T -> sum + element } cumulative(zero) { element: T, sum: T -> sum + element }
} }

View File

@ -0,0 +1,37 @@
package scientifik.kmath.coroutines
import scientifik.kmath.operations.RealField
import scientifik.kmath.operations.SpaceOperations
import kotlin.jvm.JvmName
/**
* A suspendable univariate function defined in algebraic context
*/
interface UFunction<T, C : SpaceOperations<T>> {
suspend operator fun C.invoke(arg: T): T
}
suspend fun UFunction<Double, RealField>.invoke(arg: Double) = RealField.invoke(arg)
/**
* A suspendable multivariate (N->1) function defined on algebraic context
*/
interface MFunction<T, C : SpaceOperations<T>> {
/**
* The input dimension of the function
*/
val dimension: UInt
suspend operator fun C.invoke(vararg args: T): T
}
suspend fun MFunction<Double, RealField>.invoke(args: DoubleArray) = RealField.invoke(*args.toTypedArray())
@JvmName("varargInvoke")
suspend fun MFunction<Double, RealField>.invoke(vararg args: Double) = RealField.invoke(*args.toTypedArray())
/**
* A suspendable univariate function with parameter
*/
interface ParametricUFunction<T, P, C : SpaceOperations<T>> {
suspend operator fun C.invoke(arg: T, parameter: P): T
}

View File

@ -1,7 +1,19 @@
package scientifik.kmath.operations package scientifik.kmath.operations
@DslMarker
annotation class KMathContext
interface SpaceOperations<T> { /**
* Marker interface for any algebra
*/
interface Algebra
inline operator fun <T : Algebra, R> T.invoke(block: T.() -> R): R = run(block)
/**
* Space-like operations without neutral element
*/
interface SpaceOperations<T> : Algebra {
/** /**
* Addition operation for two context elements * Addition operation for two context elements
*/ */
@ -38,6 +50,9 @@ interface Space<T> : SpaceOperations<T> {
val zero: T val zero: T
} }
/**
* Operations on ring without multiplication neutral element
*/
interface RingOperations<T> : SpaceOperations<T> { interface RingOperations<T> : SpaceOperations<T> {
/** /**
* Multiplication for two field elements * Multiplication for two field elements

View File

@ -1,7 +1,4 @@
package scientifik.kmath.operations package scientifik.kmath.operations
import scientifik.kmath.structures.Buffer
import scientifik.kmath.structures.asSequence
fun <T> Space<T>.sum(data : Iterable<T>): T = data.fold(zero) { left, right -> add(left,right) } fun <T> Space<T>.sum(data : Iterable<T>): T = data.fold(zero) { left, right -> add(left,right) }
fun <T> Space<T>.sum(data : Sequence<T>): T = data.fold(zero) { left, right -> add(left, right) } fun <T> Space<T>.sum(data : Sequence<T>): T = data.fold(zero) { left, right -> add(left, right) }

View File

@ -26,7 +26,7 @@ object ComplexField : ExtendedFieldOperations<Complex>, Field<Complex> {
Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re) 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 { 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) 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<Complex>, Field<Complex> {
override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2 override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2
override fun power(arg: Complex, pow: Number): Complex = 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 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) operator fun Double.plus(c: Complex) = add(this.toComplex(), c)
@ -56,13 +56,15 @@ object ComplexField : ExtendedFieldOperations<Complex>, Field<Complex> {
* Complex number class * Complex number class
*/ */
data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Complex, ComplexField>, Comparable<Complex> { data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Complex, ComplexField>, Comparable<Complex> {
constructor(re: Number, im: Number) : this(re.toDouble(), im.toDouble())
override fun unwrap(): Complex = this override fun unwrap(): Complex = this
override fun Complex.wrap(): Complex = this override fun Complex.wrap(): Complex = this
override val context: ComplexField get() = ComplexField override val context: ComplexField get() = ComplexField
override fun compareTo(other: Complex): Int = abs.compareTo(other.abs) override fun compareTo(other: Complex): Int = r.compareTo(other.r)
companion object : MemorySpec<Complex> { companion object : MemorySpec<Complex> {
override val objectSize: Int = 16 override val objectSize: Int = 16
@ -82,15 +84,10 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl
*/ */
val Complex.conjugate: Complex get() = Complex(re, -im) val Complex.conjugate: Complex get() = Complex(re, -im)
/**
* Square of the complex number
*/
val Complex.square: Double get() = re * re + im * im
/** /**
* Absolute value of complex number * Absolute value of complex number
*/ */
val Complex.abs: Double get() = sqrt(square) val Complex.r: Double get() = sqrt(re * re + im * im)
/** /**
* An angle between vector represented by complex number and X axis * An angle between vector represented by complex number and X axis

View File

@ -1,7 +1,7 @@
package scientifik.kmath.operations package scientifik.kmath.operations
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.pow import kotlin.math.pow as kpow
/** /**
* Advanced Number-like field that implements basic operations * Advanced Number-like field that implements basic operations
@ -45,7 +45,7 @@ object RealField : ExtendedField<Double>, Norm<Double, Double> {
override inline fun sin(arg: Double) = kotlin.math.sin(arg) override inline fun sin(arg: Double) = kotlin.math.sin(arg)
override inline fun cos(arg: Double) = kotlin.math.cos(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 exp(arg: Double) = kotlin.math.exp(arg)
override inline fun ln(arg: Double) = kotlin.math.ln(arg) override inline fun ln(arg: Double) = kotlin.math.ln(arg)

View File

@ -32,6 +32,8 @@ fun <T : MathElement<out TrigonometricOperations<T>>> ctg(arg: T): T = arg.conte
interface PowerOperations<T> { interface PowerOperations<T> {
fun power(arg: T, pow: Number): T fun power(arg: T, pow: Number): T
fun sqrt(arg: T) = power(arg, 0.5) fun sqrt(arg: T) = power(arg, 0.5)
infix fun T.pow(pow: Number) = power(this, pow)
} }
infix fun <T : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power) infix fun <T : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)

View File

@ -71,3 +71,12 @@ class BoxingNDField<T, F : Field<T>>(
override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> = override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> =
BufferedNDFieldElement(this@BoxingNDField, buffer) BufferedNDFieldElement(this@BoxingNDField, buffer)
} }
inline fun <T : Any, F : Field<T>, R> F.nd(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: NDField<T, F, *>.() -> R
): R {
val ndfield: BoxingNDField<T, F> = NDField.boxing(this, *shape, bufferFactory = bufferFactory)
return ndfield.action()
}

View File

@ -69,9 +69,9 @@ interface Buffer<T> {
} }
} }
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence() fun <T> Buffer<T>.asSequence(): Sequence<T> = Sequence(::iterator)
fun <T> Buffer<T>.asIterable(): Iterable<T> = iterator().asSequence().asIterable() fun <T> Buffer<T>.asIterable(): Iterable<T> = asSequence().asIterable()
interface MutableBuffer<T> : Buffer<T> { interface MutableBuffer<T> : Buffer<T> {
operator fun set(index: Int, value: T) operator fun set(index: Int, value: T)

View File

@ -135,3 +135,10 @@ fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDFiel
fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement = fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement =
NDField.complex(*shape).produce(initializer) NDField.complex(*shape).produce(initializer)
/**
* Produce a context for n-dimensional operations inside this real field
*/
inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexNDField.() -> R): R {
return NDField.complex(*shape).run(action)
}

View File

@ -2,7 +2,6 @@ package scientifik.kmath.structures
import scientifik.kmath.operations.* import scientifik.kmath.operations.*
interface ExtendedNDField<T : Any, F, N : NDStructure<T>> : interface ExtendedNDField<T : Any, F, N : NDStructure<T>> :
NDField<T, F, N>, NDField<T, F, N>,
TrigonometricOperations<N>, TrigonometricOperations<N>,

View File

@ -119,3 +119,10 @@ operator fun RealNDElement.plus(arg: Double) =
*/ */
operator fun RealNDElement.minus(arg: Double) = operator fun RealNDElement.minus(arg: Double) =
map { it - arg } map { it - arg }
/**
* Produce a context for n-dimensional operations inside this real field
*/
inline fun <R> RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R {
return NDField.real(*shape).run(action)
}

View File

@ -8,15 +8,15 @@ class MatrixTest {
@Test @Test
fun testSum() { fun testSum() {
val vector1 = Vector.real(5) { it.toDouble() } val vector1 = RealVector(5) { it.toDouble() }
val vector2 = Vector.real(5) { 5 - it.toDouble() } val vector2 = RealVector(5) { 5 - it.toDouble() }
val sum = vector1 + vector2 val sum = vector1 + vector2
assertEquals(5.0, sum[2]) assertEquals(5.0, sum[2])
} }
@Test @Test
fun testVectorToMatrix() { fun testVectorToMatrix() {
val vector = Vector.real(5) { it.toDouble() } val vector = RealVector(5) { it.toDouble() }
val matrix = vector.asMatrix() val matrix = vector.asMatrix()
assertEquals(4.0, matrix[4, 0]) assertEquals(4.0, matrix[4, 0])
} }
@ -31,8 +31,8 @@ class MatrixTest {
@Test @Test
fun testDot() { fun testDot() {
val vector1 = Vector.real(5) { it.toDouble() } val vector1 = RealVector(5) { it.toDouble() }
val vector2 = Vector.real(5) { 5 - it.toDouble() } val vector2 = RealVector(5) { 5 - it.toDouble() }
val matrix1 = vector1.asMatrix() val matrix1 = vector1.asMatrix()
val matrix2 = vector2.asMatrix().transpose() val matrix2 = vector2.asMatrix().transpose()

View File

@ -6,7 +6,7 @@ import kotlin.test.assertEquals
class RealFieldTest { class RealFieldTest {
@Test @Test
fun testSqrt() { fun testSqrt() {
val sqrt = with(RealField) { val sqrt = RealField {
sqrt(25 * one) sqrt(25 * one)
} }
assertEquals(5.0, sqrt) assertEquals(5.0, sqrt)

View File

@ -1,26 +1,23 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
id("kotlinx-atomicfu") version Versions.atomicfuVersion //id("scientifik.atomic")
} }
kotlin.sourceSets { kotlin.sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api(project(":kmath-core")) api(project(":kmath-core"))
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.coroutinesVersion}") api("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Scientifik.coroutinesVersion}")
compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Versions.atomicfuVersion}")
} }
} }
jvmMain { jvmMain {
dependencies { dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutinesVersion}") api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Scientifik.coroutinesVersion}")
compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}")
} }
} }
jsMain { jsMain {
dependencies { dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.coroutinesVersion}") api("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Scientifik.coroutinesVersion}")
compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Versions.atomicfuVersion}")
} }
} }
} }

View File

@ -16,10 +16,9 @@
package scientifik.kmath.chains package scientifik.kmath.chains
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.updateAndGet
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/** /**
@ -44,9 +43,7 @@ interface Chain<out R> {
/** /**
* Chain as a coroutine flow. The flow emit affects chain state and vice versa * Chain as a coroutine flow. The flow emit affects chain state and vice versa
*/ */
@FlowPreview fun <R> Chain<R>.flow(): Flow<R> = kotlinx.coroutines.flow.flow { while (true) emit(next()) }
val <R> Chain<R>.flow: Flow<R>
get() = kotlinx.coroutines.flow.flow { while (true) emit(next()) }
fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() } fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() }
fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain() fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain()
@ -64,16 +61,20 @@ class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> {
*/ */
class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> { class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> {
constructor(seedValue: R, gen: suspend (R) -> R) : this({ seedValue }, gen) private val mutex = Mutex()
private val value = atomic<R?>(null) private var value: R? = null
override suspend fun next(): R { 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<R> { override fun fork(): Chain<R> {
return MarkovChain(seed = { value.value ?: seed() }, gen = gen) return MarkovChain(seed = { value ?: seed() }, gen = gen)
} }
} }
@ -89,17 +90,16 @@ class StatefulChain<S, out R>(
private val gen: suspend S.(R) -> R private val gen: suspend S.(R) -> R
) : Chain<R> { ) : Chain<R> {
constructor(state: S, seedValue: R, forkState: ((S) -> S), gen: suspend S.(R) -> R) : this( private val mutex = Mutex()
state,
{ seedValue },
forkState,
gen
)
private val atomicValue = atomic<R?>(null) private var value: R? = null
override suspend fun next(): R { 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<R> { override fun fork(): Chain<R> {
@ -122,23 +122,23 @@ class ConstantChain<out T>(val value: T) : Chain<T> {
* Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed * 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 * since mapped chain consumes tokens. Accepts regular transformation function
*/ */
fun <T, R> Chain<T>.pipe(func: suspend (T) -> R): Chain<R> = object : Chain<R> { fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> = object : Chain<R> {
override suspend fun next(): R = func(this@pipe.next()) override suspend fun next(): R = func(this@map.next())
override fun fork(): Chain<R> = this@pipe.fork().pipe(func) override fun fork(): Chain<R> = this@map.fork().map(func)
} }
/** /**
* Map the whole chain * Map the whole chain
*/ */
fun <T, R> Chain<T>.map(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> { fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> {
override suspend fun next(): R = mapper(this@map) override suspend fun next(): R = mapper(this@collect)
override fun fork(): Chain<R> = this@map.fork().map(mapper) override fun fork(): Chain<R> = this@collect.fork().collect(mapper)
} }
fun <T, S, R> Chain<T>.mapWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain<T>) -> R): Chain<R> = fun <T, S, R> Chain<T>.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain<T>) -> R): Chain<R> =
object : Chain<R> { object : Chain<R> {
override suspend fun next(): R = state.mapper(this@mapWithState) override suspend fun next(): R = state.mapper(this@collectWithState)
override fun fork(): Chain<R> = this@mapWithState.fork().mapWithState(stateFork(state), stateFork, mapper) override fun fork(): Chain<R> = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper)
} }
/** /**

View File

@ -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 <T> Flow<T>.cumulativeSum(space: SpaceOperations<T>): Flow<T> = with(space) {
scanReduce { sum: T, element: T -> sum + element }
}
@ExperimentalCoroutinesApi
fun <T> Flow<T>.mean(space: Space<T>): Flow<T> = 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 }
}

View File

@ -21,8 +21,8 @@ internal class LazyDeferred<T>(val dispatcher: CoroutineDispatcher, val block: s
suspend fun await(): T = deferred?.await() ?: error("Coroutine not started") suspend fun await(): T = deferred?.await() ?: error("Coroutine not started")
} }
@FlowPreview
class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> { class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> {
@InternalCoroutinesApi
override suspend fun collect(collector: FlowCollector<T>) { override suspend fun collect(collector: FlowCollector<T>) {
deferredFlow.collect { deferredFlow.collect {
collector.emit((it.await())) collector.emit((it.await()))
@ -88,14 +88,13 @@ suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, action: suspend (value: T
}) })
} }
@ExperimentalCoroutinesApi
@FlowPreview @FlowPreview
fun <T, R> Flow<T>.map( fun <T, R> Flow<T>.mapParallel(
dispatcher: CoroutineDispatcher, dispatcher: CoroutineDispatcher = Dispatchers.Default,
concurrencyLevel: Int = 16,
bufferSize: Int = concurrencyLevel,
transform: suspend (T) -> R transform: suspend (T) -> R
): Flow<R> { ): Flow<R> {
return flatMapMerge(concurrencyLevel, bufferSize) { value -> return flatMapMerge{ value ->
flow { emit(transform(value)) } flow { emit(transform(value)) }
}.flowOn(dispatcher) }.flowOn(dispatcher)
} }

View File

@ -9,7 +9,6 @@ import scientifik.kmath.structures.DoubleBuffer
/** /**
* Create a [Flow] from buffer * Create a [Flow] from buffer
*/ */
@FlowPreview
fun <T> Buffer<T>.asFlow() = iterator().asFlow() fun <T> Buffer<T>.asFlow() = iterator().asFlow()
/** /**
@ -21,7 +20,6 @@ fun <T> Flow<Buffer<out T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() }
/** /**
* Collect incoming flow into fixed size chunks * Collect incoming flow into fixed size chunks
*/ */
@FlowPreview
fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow<Buffer<T>> = flow { fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow<Buffer<T>> = flow {
require(bufferSize > 0) { "Resulting chunk size must be more than zero" } require(bufferSize > 0) { "Resulting chunk size must be more than zero" }
val list = ArrayList<T>(bufferSize) val list = ArrayList<T>(bufferSize)
@ -45,7 +43,6 @@ fun <T> Flow<T>.chunked(bufferSize: Int, bufferFactory: BufferFactory<T>): Flow<
/** /**
* Specialized flow chunker for real buffer * Specialized flow chunker for real buffer
*/ */
@FlowPreview
fun Flow<Double>.chunked(bufferSize: Int): Flow<DoubleBuffer> = flow { fun Flow<Double>.chunked(bufferSize: Int): Flow<DoubleBuffer> = flow {
require(bufferSize > 0) { "Resulting chunk size must be more than zero" } require(bufferSize > 0) { "Resulting chunk size must be more than zero" }
val array = DoubleArray(bufferSize) val array = DoubleArray(bufferSize)
@ -69,7 +66,6 @@ fun Flow<Double>.chunked(bufferSize: Int): Flow<DoubleBuffer> = flow {
* Map a flow to a moving window buffer. The window step is one. * Map a flow to a moving window buffer. The window step is one.
* In order to get different steps, one could use skip operation. * In order to get different steps, one could use skip operation.
*/ */
@FlowPreview
fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow { fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow {
require(window > 1) { "Window size must be more than one" } require(window > 1) { "Window size must be more than one" }
val ringBuffer = RingBuffer.boxing<T>(window) val ringBuffer = RingBuffer.boxing<T>(window)

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.collect
import org.junit.Test import org.junit.Test
import scientifik.kmath.coroutines.async import scientifik.kmath.coroutines.async
import scientifik.kmath.coroutines.collect import scientifik.kmath.coroutines.collect
import scientifik.kmath.coroutines.map import scientifik.kmath.coroutines.mapParallel
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -20,8 +20,8 @@ class BufferFlowTest {
@Test(timeout = 2000) @Test(timeout = 2000)
fun map() { fun map() {
runBlocking { runBlocking {
(1..20).asFlow().map( dispatcher) { (1..20).asFlow().mapParallel( dispatcher) {
//println("Started $it on ${Thread.currentThread().name}") println("Started $it on ${Thread.currentThread().name}")
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
Thread.sleep(200) Thread.sleep(200)
it it
@ -35,7 +35,7 @@ class BufferFlowTest {
fun async() { fun async() {
runBlocking { runBlocking {
(1..20).asFlow().async(dispatcher) { (1..20).asFlow().async(dispatcher) {
//println("Started $it on ${Thread.currentThread().name}") println("Started $it on ${Thread.currentThread().name}")
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
Thread.sleep(200) Thread.sleep(200)
it it

View File

@ -1,5 +1,5 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
} }
kotlin.sourceSets.commonMain { kotlin.sourceSets.commonMain {

View File

@ -1,7 +1,7 @@
package scientifik.kmath.histogram package scientifik.kmath.histogram
import scientifik.kmath.linear.Point import scientifik.kmath.linear.Point
import scientifik.kmath.linear.toVector import scientifik.kmath.linear.asVector
import scientifik.kmath.operations.SpaceOperations import scientifik.kmath.operations.SpaceOperations
import scientifik.kmath.structures.* import scientifik.kmath.structures.*
import kotlin.math.floor import kotlin.math.floor
@ -132,8 +132,8 @@ class RealHistogram(
*/ */
fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): RealHistogram { fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): RealHistogram {
return RealHistogram( return RealHistogram(
ranges.map { it.start }.toVector(), ranges.map { it.start }.asVector(),
ranges.map { it.endInclusive }.toVector() ranges.map { it.endInclusive }.asVector()
) )
} }

View File

@ -3,7 +3,7 @@ package scietifik.kmath.histogram
import scientifik.kmath.histogram.RealHistogram import scientifik.kmath.histogram.RealHistogram
import scientifik.kmath.histogram.fill import scientifik.kmath.histogram.fill
import scientifik.kmath.histogram.put import scientifik.kmath.histogram.put
import scientifik.kmath.linear.Vector import scientifik.kmath.linear.RealVector
import kotlin.random.Random import kotlin.random.Random
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -19,9 +19,9 @@ class MultivariateHistogramTest {
) )
histogram.put(0.55, 0.55) histogram.put(0.55, 0.55)
val bin = histogram.find { it.value.toInt() > 0 }!! val bin = histogram.find { it.value.toInt() > 0 }!!
assertTrue { bin.contains(Vector.ofReal(0.55, 0.55)) } assertTrue { bin.contains(RealVector(0.55, 0.55)) }
assertTrue { bin.contains(Vector.ofReal(0.6, 0.5)) } assertTrue { bin.contains(RealVector(0.6, 0.5)) }
assertFalse { bin.contains(Vector.ofReal(-0.55, 0.55)) } assertFalse { bin.contains(RealVector(-0.55, 0.55)) }
} }
@Test @Test
@ -39,7 +39,7 @@ class MultivariateHistogramTest {
histogram.fill { histogram.fill {
repeat(n) { repeat(n) {
yield(Vector.ofReal(nextDouble(), nextDouble(), nextDouble())) yield(RealVector(nextDouble(), nextDouble(), nextDouble()))
} }
} }
assertEquals(n, histogram.sumBy { it.value.toInt() }) assertEquals(n, histogram.sumBy { it.value.toInt() })

View File

@ -1,7 +1,7 @@
package scientifik.kmath.histogram package scientifik.kmath.histogram
import scientifik.kmath.linear.RealVector import scientifik.kmath.linear.RealVector
import scientifik.kmath.linear.toVector import scientifik.kmath.linear.asVector
import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Buffer
import java.util.* import java.util.*
import kotlin.math.floor import kotlin.math.floor
@ -12,7 +12,7 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou
//TODO add weighting //TODO add weighting
override val value: Number get() = counter.sum() 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) operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2)

View File

@ -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 {
// }
}
}

View File

@ -1,5 +1,5 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
} }
repositories { repositories {

View File

@ -1,3 +1,3 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
} }

View File

@ -1,7 +1,5 @@
package scientifik.memory package scientifik.memory
import kotlin.reflect.KClass
/** /**
* A specification to read or write custom objects with fixed size in bytes * A specification to read or write custom objects with fixed size in bytes
*/ */
@ -27,7 +25,7 @@ inline fun <reified T : Any> MemoryReader.readArray(spec: MemorySpec<T>, offset:
fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>) { fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>) {
spec.run { spec.run {
for (i in 0 until array.size) { for (i in array.indices) {
write(offset + i * objectSize, array[i]) write(offset + i * objectSize, array[i])
} }
} }

View File

@ -1,30 +1,11 @@
plugins { plugins {
`npm-multiplatform` id("scientifik.mpp")
id("kotlinx-atomicfu") version Versions.atomicfuVersion
} }
kotlin.sourceSets { kotlin.sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api(project(":kmath-coroutines")) api(project(":kmath-coroutines"))
compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Versions.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}")
}
}
jsMain {
dependencies {
compileOnly("org.jetbrains.kotlinx:atomicfu-js:${Versions.atomicfuVersion}")
}
}
}
atomicfu {
variant = "VH"
} }

View File

@ -1,8 +1,9 @@
package scientifik.kmath.prob package scientifik.kmath.prob
import scientifik.kmath.chains.Chain import scientifik.kmath.chains.Chain
import scientifik.kmath.chains.map import scientifik.kmath.chains.collect
import kotlin.jvm.JvmName import scientifik.kmath.structures.Buffer
import scientifik.kmath.structures.BufferFactory
interface Sampler<T : Any> { interface Sampler<T : Any> {
fun sample(generator: RandomGenerator): Chain<T> fun sample(generator: RandomGenerator): Chain<T>
@ -20,7 +21,7 @@ interface Distribution<T : Any> : Sampler<T> {
/** /**
* Create a chain of samples from this distribution. * 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<T> override fun sample(generator: RandomGenerator): Chain<T>
@ -32,7 +33,7 @@ interface Distribution<T : Any> : Sampler<T> {
interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> { interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> {
/** /**
* Cumulative distribution for ordered parameter * Cumulative distribution for ordered parameter (CDF)
*/ */
fun cumulative(arg: T): Double fun cumulative(arg: T): Double
} }
@ -45,24 +46,27 @@ fun <T : Comparable<T>> UnivariateDistribution<T>.integral(from: T, to: T): Doub
return cumulative(to) - cumulative(from) return cumulative(to) - cumulative(from)
} }
/** /**
* Sample a bunch of values * Sample a bunch of values
*/ */
fun <T : Any> Sampler<T>.sampleBunch(generator: RandomGenerator, size: Int): Chain<List<T>> { fun <T : Any> Sampler<T>.sampleBuffer(
generator: RandomGenerator,
size: Int,
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing
): Chain<Buffer<T>> {
require(size > 1) require(size > 1)
return sample(generator).map{chain -> //creating temporary storage once
List(size){chain.next()} val tmp = ArrayList<T>(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 * Generate a bunch of samples from real distributions
*/ */
@JvmName("realSampleBunch") fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int) =
fun Sampler<Double>.sampleBunch(generator: RandomGenerator, size: Int): Chain<DoubleArray> { sampleBuffer(generator, size, Buffer.Companion::real)
require(size > 1)
return sample(generator).map{chain ->
DoubleArray(size){chain.next()}
}
}

View File

@ -0,0 +1,47 @@
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<T> : Distribution<Map<String, T>>
/**
* A multivariate distribution that has independent distributions for separate axis
*/
class FactorizedDistribution<T>(val distributions: Collection<NamedDistribution<T>>) : NamedDistribution<T> {
override fun probability(arg: Map<String, T>): Double {
return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) }
}
override fun sample(generator: RandomGenerator): Chain<Map<String, T>> {
val chains = distributions.map { it.sample(generator) }
return SimpleChain<Map<String, T>> {
chains.fold(emptyMap()) { acc, chain -> acc + chain.next() }
}
}
}
class NamedDistributionWrapper<T : Any>(val name: String, val distribution: Distribution<T>) : NamedDistribution<T> {
override fun probability(arg: Map<String, T>): Double = distribution.probability(
arg[name] ?: error("Argument with name $name not found in input parameters")
)
override fun sample(generator: RandomGenerator): Chain<Map<String, T>> {
val chain = distribution.sample(generator)
return SimpleChain {
mapOf(name to chain.next())
}
}
}
class DistributionBuilder<T: Any>{
private val distributions = ArrayList<NamedDistribution<T>>()
infix fun String.to(distribution: Distribution<T>){
distributions.add(NamedDistributionWrapper(this,distribution))
}
}

View File

@ -1,6 +1,5 @@
package scientifik.kmath.prob package scientifik.kmath.prob
import kotlinx.atomicfu.atomic
import scientifik.kmath.chains.Chain import scientifik.kmath.chains.Chain
/** /**
@ -11,3 +10,6 @@ class RandomChain<out R>(val generator: RandomGenerator, private val gen: suspen
override fun fork(): Chain<R> = RandomChain(generator.fork(), gen) override fun fork(): Chain<R> = RandomChain(generator.fork(), gen)
} }
fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen)
fun <R> RandomGenerator.flow(gen: suspend RandomGenerator.() -> R) = chain(gen).fork()

View File

@ -2,7 +2,7 @@ package scientifik.kmath.prob
import scientifik.kmath.chains.Chain import scientifik.kmath.chains.Chain
import scientifik.kmath.chains.ConstantChain import scientifik.kmath.chains.ConstantChain
import scientifik.kmath.chains.pipe import scientifik.kmath.chains.map
import scientifik.kmath.chains.zip import scientifik.kmath.chains.zip
import scientifik.kmath.operations.Space import scientifik.kmath.operations.Space
@ -26,6 +26,6 @@ class SamplerSpace<T : Any>(val space: Space<T>) : Space<Sampler<T>> {
} }
override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator -> override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator ->
a.sample(generator).pipe { space.run { it * k.toDouble() } } a.sample(generator).map { space.run { it * k.toDouble() } }
} }
} }

View File

@ -0,0 +1,94 @@
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<T, R> {
suspend operator fun invoke(data: Buffer<T>): R
}
/**
* 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<T, I, R> : Statistic<T, R> {
//compute statistic on a single block
suspend fun computeIntermediate(data: Buffer<T>): 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<T>): R = toResult(computeIntermediate(data))
}
@FlowPreview
@ExperimentalCoroutinesApi
private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate(
flow: Flow<Buffer<T>>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<I> = 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 <T, I, R> ComposableStatistic<T, I, R>.flow(
flow: Flow<Buffer<T>>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<R> = flowIntermediate(flow,dispatcher).map(::toResult)
/**
* Arithmetic mean
*/
class Mean<T>(val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> {
override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> =
space.run { sum(data.asIterable()) } to data.size
override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> =
space.run { first.first + second.first } to (first.second + second.second)
override suspend fun toResult(intermediate: Pair<T, Int>): 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<T>(comparator: Comparator<T>) : Statistic<T, T> {
override suspend fun invoke(data: Buffer<T>): 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) })
}
}

View File

@ -0,0 +1,31 @@
package scientifik.kmath.prob
import scientifik.kmath.chains.Chain
import scientifik.kmath.chains.SimpleChain
class UniformDistribution(val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> {
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<Double> {
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
}
}
}

View File

@ -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)
}
}
}

View File

@ -0,0 +1,10 @@
plugins {
id("scientifik.jvm")
}
description = "Binding for https://github.com/JetBrains-Research/viktor"
dependencies {
api(project(":kmath-core"))
api("org.jetbrains.bio:viktor:1.0.1")
}

View File

@ -0,0 +1,20 @@
package scientifik.kmath.viktor
import org.jetbrains.bio.viktor.F64FlatArray
import scientifik.kmath.structures.MutableBuffer
@Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE")
inline class ViktorBuffer(val flatArray: F64FlatArray) : MutableBuffer<Double> {
override val size: Int get() = flatArray.size
override inline fun get(index: Int): Double = flatArray[index]
override inline fun set(index: Int, value: Double) {
flatArray[index] = value
}
override fun copy(): MutableBuffer<Double> {
return ViktorBuffer(flatArray.copy().flatten())
}
override fun iterator(): Iterator<Double> = flatArray.data.iterator()
}

View File

@ -0,0 +1,86 @@
package scientifik.kmath.viktor
import org.jetbrains.bio.viktor.F64Array
import scientifik.kmath.operations.RealField
import scientifik.kmath.structures.DefaultStrides
import scientifik.kmath.structures.MutableNDStructure
import scientifik.kmath.structures.NDField
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
inline class ViktorNDStructure(val f64Buffer: F64Array) : MutableNDStructure<Double> {
override val shape: IntArray get() = f64Buffer.shape
override inline fun get(index: IntArray): Double = f64Buffer.get(*index)
override inline fun set(index: IntArray, value: Double) {
f64Buffer.set(*index, value = value)
}
override fun elements(): Sequence<Pair<IntArray, Double>> {
return DefaultStrides(shape).indices().map { it to get(it) }
}
}
fun F64Array.asStructure(): ViktorNDStructure = ViktorNDStructure(this)
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
class ViktorNDField(override val shape: IntArray) : NDField<Double, RealField, ViktorNDStructure> {
override val zero: ViktorNDStructure
get() = F64Array.full(init = 0.0, shape = *shape).asStructure()
override val one: ViktorNDStructure
get() = F64Array.full(init = 1.0, shape = *shape).asStructure()
val strides = DefaultStrides(shape)
override val elementContext: RealField get() = RealField
override fun produce(initializer: RealField.(IntArray) -> Double): ViktorNDStructure = F64Array(*shape).apply {
this@ViktorNDField.strides.indices().forEach { index ->
set(value = RealField.initializer(index), indices = *index)
}
}.asStructure()
override fun map(arg: ViktorNDStructure, transform: RealField.(Double) -> Double): ViktorNDStructure =
F64Array(*shape).apply {
this@ViktorNDField.strides.indices().forEach { index ->
set(value = RealField.transform(arg[index]), indices = *index)
}
}.asStructure()
override fun mapIndexed(
arg: ViktorNDStructure,
transform: RealField.(index: IntArray, Double) -> Double
): ViktorNDStructure = F64Array(*shape).apply {
this@ViktorNDField.strides.indices().forEach { index ->
set(value = RealField.transform(index, arg[index]), indices = *index)
}
}.asStructure()
override fun combine(
a: ViktorNDStructure,
b: ViktorNDStructure,
transform: RealField.(Double, Double) -> Double
): ViktorNDStructure = F64Array(*shape).apply {
this@ViktorNDField.strides.indices().forEach { index ->
set(value = RealField.transform(a[index], b[index]), indices = *index)
}
}.asStructure()
override fun add(a: ViktorNDStructure, b: ViktorNDStructure): ViktorNDStructure {
return (a.f64Buffer + b.f64Buffer).asStructure()
}
override fun multiply(a: ViktorNDStructure, k: Number): ViktorNDStructure =
(a.f64Buffer * k.toDouble()).asStructure()
override inline fun ViktorNDStructure.plus(b: ViktorNDStructure): ViktorNDStructure =
(f64Buffer + b.f64Buffer).asStructure()
override inline fun ViktorNDStructure.minus(b: ViktorNDStructure): ViktorNDStructure =
(f64Buffer - b.f64Buffer).asStructure()
override inline fun ViktorNDStructure.times(k: Number): ViktorNDStructure = (f64Buffer * k.toDouble()).asStructure()
override inline fun ViktorNDStructure.plus(arg: Double): ViktorNDStructure = (f64Buffer.plus(arg)).asStructure()
}

View File

@ -1,24 +1,30 @@
pluginManagement { pluginManagement {
plugins {
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 { repositories {
mavenLocal()
jcenter() jcenter()
gradlePluginPortal() gradlePluginPortal()
maven("https://dl.bintray.com/kotlin/kotlin-eap") 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 { resolutionStrategy {
eachPlugin { eachPlugin {
when (requested.id.id) { when (requested.id.id) {
"kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}") "scientifik.mpp", "scientifik.publish" -> useModule("scientifik:gradle-tools:${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")
} }
} }
} }
} }
enableFeaturePreview("GRADLE_METADATA")
rootProject.name = "kmath" rootProject.name = "kmath"
include( include(
":kmath-memory", ":kmath-memory",
@ -27,8 +33,10 @@ include(
":kmath-coroutines", ":kmath-coroutines",
":kmath-histograms", ":kmath-histograms",
":kmath-commons", ":kmath-commons",
":kmath-viktor",
":kmath-koma", ":kmath-koma",
":kmath-prob", ":kmath-prob",
":kmath-io",
":kmath-dimensions", ":kmath-dimensions",
":examples" ":examples"
) )