forked from kscience/kmath
Merge branch 'dev' into dimensions
# Conflicts: # build.gradle.kts # buildSrc/build.gradle.kts # settings.gradle.kts
This commit is contained in:
commit
a156feb397
17
.github/workflows/gradle.yml
vendored
Normal file
17
.github/workflows/gradle.yml
vendored
Normal 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
|
81
README.md
81
README.md
@ -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-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)
|
||||
@ -46,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{
|
||||
@ -105,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.
|
||||
|
@ -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 {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven("https://kotlin.bintray.com/kotlinx")
|
||||
maven("https://dl.bintray.com/kotlin/kotlinx")
|
||||
}
|
||||
|
||||
group = "scientifik"
|
||||
@ -11,8 +18,7 @@ allprojects {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "dokka-publish")
|
||||
if (name.startsWith("kmath")) {
|
||||
apply(plugin = "npm-publish")
|
||||
apply(plugin = "scientifik.publish")
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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"
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
@ -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.61"
|
||||
id("kotlinx.benchmark") version "0.2.0-dev-5"
|
||||
}
|
||||
|
||||
configure<AllOpenExtension> {
|
||||
@ -16,7 +15,8 @@ configure<AllOpenExtension> {
|
||||
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()
|
||||
}
|
||||
|
||||
@ -29,10 +29,11 @@ dependencies {
|
||||
implementation(project(":kmath-coroutines"))
|
||||
implementation(project(":kmath-commons"))
|
||||
implementation(project(":kmath-koma"))
|
||||
implementation(project(":kmath-viktor"))
|
||||
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 +44,7 @@ benchmark {
|
||||
// Setup configurations
|
||||
targets {
|
||||
// This one matches sourceSet name above
|
||||
register("benchmarks") {
|
||||
this as JvmBenchmarkTarget
|
||||
jmhVersion = "1.21"
|
||||
}
|
||||
register("benchmarks")
|
||||
}
|
||||
|
||||
configurations {
|
||||
@ -62,6 +60,6 @@ benchmark {
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = Scientifik.JVM_VERSION
|
||||
}
|
||||
}
|
@ -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<Double>.mean(): Chain<Double> = mapWithState(AveragingChainState(),{it.copy()}){chain->
|
||||
fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(),{it.copy()}){ chain->
|
||||
val next = chain.next()
|
||||
num++
|
||||
value += next
|
||||
return@mapWithState value / num
|
||||
return@collectWithState value / num
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
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
|
||||
|
||||
fun main() {
|
||||
@ -32,3 +36,25 @@ fun main() {
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,13 @@ import kotlinx.coroutines.GlobalScope
|
||||
import scientifik.kmath.operations.RealField
|
||||
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 n = 1000
|
||||
|
||||
@ -15,8 +21,7 @@ fun main(args: Array<String>) {
|
||||
//A generic boxing field. It should be used for objects, not primitives.
|
||||
val genericField = NDField.boxing(RealField, dim, dim)
|
||||
|
||||
|
||||
val autoTime = measureTimeMillis {
|
||||
measureAndPrint("Automatic field addition") {
|
||||
autoField.run {
|
||||
var res = one
|
||||
repeat(n) {
|
||||
@ -25,18 +30,14 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
println("Automatic field addition completed in $autoTime millis")
|
||||
|
||||
val elementTime = measureTimeMillis {
|
||||
measureAndPrint("Element addition"){
|
||||
var res = genericField.one
|
||||
repeat(n) {
|
||||
res += 1.0
|
||||
}
|
||||
}
|
||||
|
||||
println("Element addition completed in $elementTime millis")
|
||||
|
||||
val specializedTime = measureTimeMillis {
|
||||
measureAndPrint("Specialized addition") {
|
||||
specializedField.run {
|
||||
var res: NDBuffer<Double> = one
|
||||
repeat(n) {
|
||||
@ -45,10 +46,7 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
println("Specialized addition completed in $specializedTime millis")
|
||||
|
||||
|
||||
val lazyTime = measureTimeMillis {
|
||||
measureAndPrint("Lazy addition") {
|
||||
val res = specializedField.one.mapAsync(GlobalScope) {
|
||||
var c = 0.0
|
||||
repeat(n) {
|
||||
@ -60,9 +58,7 @@ fun main(args: Array<String>) {
|
||||
res.elements().forEach { it.second }
|
||||
}
|
||||
|
||||
println("Lazy addition completed in $lazyTime millis")
|
||||
|
||||
val genericTime = measureTimeMillis {
|
||||
measureAndPrint("Generic addition") {
|
||||
//genericField.run(action)
|
||||
genericField.run {
|
||||
var res: NDBuffer<Double> = one
|
||||
@ -72,6 +68,4 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
println("Generic addition completed in $genericTime millis")
|
||||
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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-6.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
35
gradlew
vendored
35
gradlew
vendored
@ -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,
|
||||
@ -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"`
|
||||
@ -154,19 +154,19 @@ if $cygwin ; then
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@ -175,14 +175,9 @@ save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# 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"
|
||||
|
||||
# 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" "$@"
|
||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@ -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,
|
||||
|
@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
`maven-publish`
|
||||
id("scientifik.jvm")
|
||||
}
|
||||
|
||||
description = "Commons math binding for kmath"
|
||||
@ -10,21 +9,4 @@ dependencies {
|
||||
api(project(":kmath-coroutines"))
|
||||
api(project(":kmath-prob"))
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ import scientifik.kmath.structures.*
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Streaming and buffer transformations
|
||||
*/
|
||||
object Transformations {
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
@ -8,6 +8,4 @@ kotlin.sourceSets {
|
||||
api(project(":kmath-memory"))
|
||||
}
|
||||
}
|
||||
//mingwMain {}
|
||||
//mingwTest {}
|
||||
}
|
@ -183,7 +183,7 @@ fun <T : Any> LUPDecomposition<T>.solve(type: KClass<T>, matrix: Matrix<T>): 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)
|
||||
|
@ -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<T> = Buffer<T>
|
||||
|
||||
/**
|
||||
* 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>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
||||
/**
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
@ -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() }
|
||||
}
|
||||
|
@ -10,29 +10,32 @@ import kotlin.jvm.JvmName
|
||||
* @param R type of resulting iterable
|
||||
* @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
|
||||
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 <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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
//Cumulative sum
|
||||
|
||||
/**
|
||||
* Cumulative sum with custom space
|
||||
*/
|
||||
fun <T> Iterable<T>.cumulativeSum(space: Space<T>) = with(space) {
|
||||
cumulative(zero) { element: T, sum: T -> sum + element }
|
||||
}
|
@ -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
|
||||
}
|
@ -1,7 +1,19 @@
|
||||
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
|
||||
*/
|
||||
@ -38,6 +50,9 @@ interface Space<T> : SpaceOperations<T> {
|
||||
val zero: T
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations on ring without multiplication neutral element
|
||||
*/
|
||||
interface RingOperations<T> : SpaceOperations<T> {
|
||||
/**
|
||||
* Multiplication for two field elements
|
||||
|
@ -1,7 +1,4 @@
|
||||
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 : Sequence<T>): T = data.fold(zero) { left, right -> add(left, right) }
|
@ -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)
|
||||
|
||||
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<Complex>, Field<Complex> {
|
||||
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)
|
||||
|
||||
@ -56,13 +56,15 @@ object ComplexField : ExtendedFieldOperations<Complex>, Field<Complex> {
|
||||
* Complex number class
|
||||
*/
|
||||
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 Complex.wrap(): Complex = this
|
||||
|
||||
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> {
|
||||
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)
|
||||
|
||||
/**
|
||||
* Square of the complex number
|
||||
*/
|
||||
val Complex.square: Double get() = re * re + im * im
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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<Double>, Norm<Double, Double> {
|
||||
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)
|
||||
|
@ -32,6 +32,8 @@ fun <T : MathElement<out TrigonometricOperations<T>>> ctg(arg: T): T = arg.conte
|
||||
interface PowerOperations<T> {
|
||||
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 : MathElement<out PowerOperations<T>>> T.pow(power: Double): T = context.power(this, power)
|
||||
@ -48,7 +50,7 @@ interface ExponentialOperations<T> {
|
||||
fun <T : MathElement<out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg)
|
||||
fun <T : MathElement<out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg)
|
||||
|
||||
interface Norm<in T: Any, out R> {
|
||||
interface Norm<in T : Any, out R> {
|
||||
fun norm(arg: T): R
|
||||
}
|
||||
|
||||
|
@ -71,3 +71,12 @@ class BoxingNDField<T, F : Field<T>>(
|
||||
override fun NDBuffer<T>.toElement(): FieldElement<NDBuffer<T>, *, out BufferedNDField<T, F>> =
|
||||
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()
|
||||
}
|
@ -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> {
|
||||
operator fun set(index: Int, value: T)
|
||||
|
@ -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 =
|
||||
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)
|
||||
}
|
@ -2,7 +2,6 @@ package scientifik.kmath.structures
|
||||
|
||||
import scientifik.kmath.operations.*
|
||||
|
||||
|
||||
interface ExtendedNDField<T : Any, F, N : NDStructure<T>> :
|
||||
NDField<T, F, N>,
|
||||
TrigonometricOperations<N>,
|
||||
|
@ -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 <R> RealField.nd(vararg shape: Int, action: RealNDField.() -> R): R {
|
||||
return NDField.real(*shape).run(action)
|
||||
}
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -1,26 +1,23 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("kotlinx-atomicfu") version Versions.atomicfuVersion
|
||||
id("scientifik.mpp")
|
||||
//id("scientifik.atomic")
|
||||
}
|
||||
|
||||
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}")
|
||||
}
|
||||
}
|
||||
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}")
|
||||
}
|
||||
}
|
||||
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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,9 @@
|
||||
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
@ -44,9 +43,7 @@ interface Chain<out R> {
|
||||
/**
|
||||
* Chain as a coroutine flow. The flow emit affects chain state and vice versa
|
||||
*/
|
||||
@FlowPreview
|
||||
val <R> Chain<R>.flow: Flow<R>
|
||||
get() = kotlinx.coroutines.flow.flow { while (true) emit(next()) }
|
||||
fun <R> Chain<R>.flow(): Flow<R> = kotlinx.coroutines.flow.flow { while (true) emit(next()) }
|
||||
|
||||
fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() }
|
||||
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> {
|
||||
|
||||
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 {
|
||||
return value.updateAndGet { prev -> gen(prev ?: seed()) }!!
|
||||
mutex.withLock {
|
||||
val newValue = gen(value ?: seed())
|
||||
value = newValue
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
) : Chain<R> {
|
||||
|
||||
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<R?>(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<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
|
||||
* since mapped chain consumes tokens. Accepts regular transformation function
|
||||
*/
|
||||
fun <T, R> Chain<T>.pipe(func: suspend (T) -> R): Chain<R> = object : Chain<R> {
|
||||
override suspend fun next(): R = func(this@pipe.next())
|
||||
override fun fork(): Chain<R> = this@pipe.fork().pipe(func)
|
||||
fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> = object : Chain<R> {
|
||||
override suspend fun next(): R = func(this@map.next())
|
||||
override fun fork(): Chain<R> = this@map.fork().map(func)
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the whole chain
|
||||
*/
|
||||
fun <T, R> Chain<T>.map(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> {
|
||||
override suspend fun next(): R = mapper(this@map)
|
||||
override fun fork(): Chain<R> = this@map.fork().map(mapper)
|
||||
fun <T, R> Chain<T>.collect(mapper: suspend (Chain<T>) -> R): Chain<R> = object : Chain<R> {
|
||||
override suspend fun next(): R = mapper(this@collect)
|
||||
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> {
|
||||
override suspend fun next(): R = state.mapper(this@mapWithState)
|
||||
override fun fork(): Chain<R> = this@mapWithState.fork().mapWithState(stateFork(state), stateFork, mapper)
|
||||
override suspend fun next(): R = state.mapper(this@collectWithState)
|
||||
override fun fork(): Chain<R> = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 }
|
||||
}
|
@ -21,8 +21,8 @@ internal class LazyDeferred<T>(val dispatcher: CoroutineDispatcher, val block: s
|
||||
suspend fun await(): T = deferred?.await() ?: error("Coroutine not started")
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> {
|
||||
@InternalCoroutinesApi
|
||||
override suspend fun collect(collector: FlowCollector<T>) {
|
||||
deferredFlow.collect {
|
||||
collector.emit((it.await()))
|
||||
@ -88,14 +88,13 @@ suspend fun <T> AsyncFlow<T>.collect(concurrency: Int, action: suspend (value: T
|
||||
})
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@FlowPreview
|
||||
fun <T, R> Flow<T>.map(
|
||||
dispatcher: CoroutineDispatcher,
|
||||
concurrencyLevel: Int = 16,
|
||||
bufferSize: Int = concurrencyLevel,
|
||||
fun <T, R> Flow<T>.mapParallel(
|
||||
dispatcher: CoroutineDispatcher = Dispatchers.Default,
|
||||
transform: suspend (T) -> R
|
||||
): Flow<R> {
|
||||
return flatMapMerge(concurrencyLevel, bufferSize) { value ->
|
||||
return flatMapMerge{ value ->
|
||||
flow { emit(transform(value)) }
|
||||
}.flowOn(dispatcher)
|
||||
}
|
@ -9,7 +9,6 @@ import scientifik.kmath.structures.DoubleBuffer
|
||||
/**
|
||||
* Create a [Flow] from buffer
|
||||
*/
|
||||
@FlowPreview
|
||||
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
|
||||
*/
|
||||
@FlowPreview
|
||||
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" }
|
||||
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
|
||||
*/
|
||||
@FlowPreview
|
||||
fun Flow<Double>.chunked(bufferSize: Int): Flow<DoubleBuffer> = flow {
|
||||
require(bufferSize > 0) { "Resulting chunk size must be more than zero" }
|
||||
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.
|
||||
* In order to get different steps, one could use skip operation.
|
||||
*/
|
||||
@FlowPreview
|
||||
fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow {
|
||||
require(window > 1) { "Window size must be more than one" }
|
||||
val ringBuffer = RingBuffer.boxing<T>(window)
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
kotlin.sourceSets.commonMain {
|
||||
|
@ -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<Double>): RealHistogram {
|
||||
return RealHistogram(
|
||||
ranges.map { it.start }.toVector(),
|
||||
ranges.map { it.endInclusive }.toVector()
|
||||
ranges.map { it.start }.asVector(),
|
||||
ranges.map { it.endInclusive }.asVector()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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() })
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
// }
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -1,3 +1,3 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
@ -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 <reified T : Any> MemoryReader.readArray(spec: MemorySpec<T>, offset:
|
||||
|
||||
fun <T : Any> MemoryWriter.writeArray(spec: MemorySpec<T>, offset: Int, array: Array<T>) {
|
||||
spec.run {
|
||||
for (i in 0 until array.size) {
|
||||
for (i in array.indices) {
|
||||
write(offset + i * objectSize, array[i])
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,11 @@
|
||||
plugins {
|
||||
`npm-multiplatform`
|
||||
id("kotlinx-atomicfu") version Versions.atomicfuVersion
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
kotlin.sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
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"
|
||||
}
|
@ -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<T : Any> {
|
||||
fun sample(generator: RandomGenerator): Chain<T>
|
||||
@ -20,7 +21,7 @@ interface Distribution<T : Any> : Sampler<T> {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
||||
@ -32,7 +33,7 @@ interface Distribution<T : Any> : Sampler<T> {
|
||||
|
||||
interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> {
|
||||
/**
|
||||
* Cumulative distribution for ordered parameter
|
||||
* Cumulative distribution for ordered parameter (CDF)
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
return sample(generator).map{chain ->
|
||||
List(size){chain.next()}
|
||||
//creating temporary storage once
|
||||
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
|
||||
*/
|
||||
@JvmName("realSampleBunch")
|
||||
fun Sampler<Double>.sampleBunch(generator: RandomGenerator, size: Int): Chain<DoubleArray> {
|
||||
require(size > 1)
|
||||
return sample(generator).map{chain ->
|
||||
DoubleArray(size){chain.next()}
|
||||
}
|
||||
}
|
||||
fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int) =
|
||||
sampleBuffer(generator, size, Buffer.Companion::real)
|
@ -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))
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package scientifik.kmath.prob
|
||||
|
||||
import kotlinx.atomicfu.atomic
|
||||
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)
|
||||
}
|
||||
|
||||
fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen)
|
||||
fun <R> RandomGenerator.flow(gen: suspend RandomGenerator.() -> R) = chain(gen).fork()
|
@ -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<T : Any>(val space: Space<T>) : Space<Sampler<T>> {
|
||||
}
|
||||
|
||||
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() } }
|
||||
}
|
||||
}
|
@ -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) })
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
10
kmath-viktor/build.gradle.kts
Normal file
10
kmath-viktor/build.gradle.kts
Normal 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")
|
||||
}
|
@ -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()
|
||||
}
|
@ -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()
|
||||
}
|
@ -1,24 +1,30 @@
|
||||
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 {
|
||||
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 {
|
||||
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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enableFeaturePreview("GRADLE_METADATA")
|
||||
|
||||
rootProject.name = "kmath"
|
||||
include(
|
||||
":kmath-memory",
|
||||
@ -27,8 +33,10 @@ include(
|
||||
":kmath-coroutines",
|
||||
":kmath-histograms",
|
||||
":kmath-commons",
|
||||
":kmath-viktor",
|
||||
":kmath-koma",
|
||||
":kmath-prob",
|
||||
":kmath-io",
|
||||
":kmath-dimensions",
|
||||
":examples"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user