Merge branch 'dev' into dev-0.3.2

# Conflicts:
#	build.gradle.kts
#	gradle/wrapper/gradle-wrapper.properties
#	kmath-core/build.gradle.kts
#	kmath-tensors/build.gradle.kts
This commit is contained in:
Alexander Nozik 2023-06-11 09:10:31 +03:00
commit c0a7cff1d8
37 changed files with 873 additions and 218 deletions
.gitignore.space.ktsCHANGELOG.mdREADME.md
benchmarks
build.gradle.kts
buildSrc
settings.gradle.kts
src/main/kotlin/space/kscience/kmath/benchmarks
examples
build.gradle.kts
src/main/kotlin/space/kscience/kmath/expressions
gradle.properties
gradle/wrapper
kmath-ast/src
jsTest/kotlin/space/kscience/kmath/ast
jvmTest/kotlin/space/kscience/kmath/ast
kmath-complex
kmath-core
api
build.gradle.kts
src/commonMain/kotlin/space/kscience/kmath
kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming
kmath-functions
kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry
kmath-jafama
kmath-jupyter
kmath-kotlingrad
build.gradle.kts
src/test/kotlin/space/kscience/kmath/kotlingrad
kmath-memory
kmath-optimization
kmath-stat/src
commonMain/kotlin/space/kscience/kmath
commonTest/kotlin/space/kscience/kmath/series
kmath-tensors
kmath-viktor/api
test-utils

1
.gitignore vendored

@ -4,6 +4,7 @@ out/
.idea/ .idea/
.vscode/ .vscode/
.fleet/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)

@ -1,3 +1,48 @@
import kotlin.io.path.readText
val projectName = "kmath"
job("Build") { job("Build") {
gradlew("openjdk:11", "build") //Perform only jvm tests
gradlew("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3", "test", "jvmTest")
}
job("Publish") {
startOn {
gitPush { enabled = false }
}
container("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3") {
env["SPACE_USER"] = "{{ project:space_user }}"
env["SPACE_TOKEN"] = "{{ project:space_token }}"
kotlinScript { api ->
val spaceUser = System.getenv("SPACE_USER")
val spaceToken = System.getenv("SPACE_TOKEN")
// write the version to the build directory
api.gradlew("version")
//read the version from build file
val version = java.nio.file.Path.of("build/project-version.txt").readText()
val revisionSuffix = if (version.endsWith("SNAPSHOT")) {
"-" + api.gitRevision().take(7)
} else {
""
}
api.space().projects.automation.deployments.start(
project = api.projectIdentifier(),
targetIdentifier = TargetIdentifier.Key(projectName),
version = version+revisionSuffix,
// automatically update deployment status based on the status of a job
syncWithAutomationJob = true
)
api.gradlew(
"publishAllPublicationsToSpaceRepository",
"-Ppublishing.space.user=\"$spaceUser\"",
"-Ppublishing.space.token=\"$spaceToken\"",
)
}
}
} }

@ -15,7 +15,7 @@
### Security ### Security
## 0.3.1-dev-RC - 2023-04-09 ## 0.3.1 - 2023-04-09
### Added ### Added
- Wasm support for `memory`, `core`, `complex` and `functions` modules. - Wasm support for `memory`, `core`, `complex` and `functions` modules.

@ -2,7 +2,7 @@
[![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382)
![Gradle build](https://github.com/SciProgCentre/kmath/workflows/Gradle%20build/badge.svg) ![Gradle build](https://github.com/SciProgCentre/kmath/workflows/Gradle%20build/badge.svg)
[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) [![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22)
[![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/) [![Space](https://img.shields.io/badge/dynamic/xml?color=orange&label=Space&query=//metadata/versioning/latest&url=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fspace%2Fkscience%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/spc/p/sci/maven/space/kscience/)
# KMath # KMath
@ -297,5 +297,4 @@ Gradle `6.0+` is required for multiplatform artifacts.
The project requires a lot of additional work. The most important thing we need is a feedback about what features are The project requires a lot of additional work. The most important thing we need is a feedback about what features are
required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues required the most. Feel free to create feature requests. We are also welcome to code contributions, especially in issues
marked with marked with [waiting for a hero](https://github.com/SciProgCentre/kmath/labels/waiting%20for%20a%20hero) label.
[waiting for a hero](https://github.com/mipt-npm/kmath/labels/waiting%20for%20a%20hero) label.

@ -29,6 +29,9 @@ kotlin {
all { all {
languageSettings { languageSettings {
progressiveMode = true progressiveMode = true
optIn("kotlin.contracts.ExperimentalContracts")
optIn("kotlin.ExperimentalUnsignedTypes")
optIn("space.kscience.kmath.UnstableKMathAPI")
} }
} }
@ -153,7 +156,7 @@ kotlin.sourceSets.all {
with(languageSettings) { with(languageSettings) {
optIn("kotlin.contracts.ExperimentalContracts") optIn("kotlin.contracts.ExperimentalContracts")
optIn("kotlin.ExperimentalUnsignedTypes") optIn("kotlin.ExperimentalUnsignedTypes")
optIn("space.kscience.kmath.misc.UnstableKMathAPI") optIn("space.kscience.kmath.UnstableKMathAPI")
} }
} }

@ -15,7 +15,7 @@ allprojects {
} }
group = "space.kscience" group = "space.kscience"
version = "0.3.2-dev-" version = "0.3.2-dev-1"
} }
subprojects { subprojects {
@ -66,10 +66,10 @@ ksciencePublish {
} }
github("kmath", "SciProgCentre") github("kmath", "SciProgCentre")
space( space(
if (findProperty("production") == "true" || !isInDevelopment) { if (isInDevelopment) {
"https://maven.pkg.jetbrains.space/spc/p/sci/maven"
} else {
"https://maven.pkg.jetbrains.space/spc/p/sci/dev" "https://maven.pkg.jetbrains.space/spc/p/sci/dev"
} else {
"https://maven.pkg.jetbrains.space/spc/p/sci/maven"
} }
) )
sonatype() sonatype()

@ -2,6 +2,7 @@
* Copyright 2018-2021 KMath contributors. * Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
rootProject.name = "kmath"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

@ -5,9 +5,9 @@
package space.kscience.kmath.benchmarks package space.kscience.kmath.benchmarks
import kotlinx.benchmark.gradle.BenchmarksExtension
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.benchmark.gradle.BenchmarksExtension
import org.gradle.api.Project import org.gradle.api.Project
import space.kscience.gradle.KScienceReadmeExtension import space.kscience.gradle.KScienceReadmeExtension
import java.time.LocalDateTime import java.time.LocalDateTime
@ -16,6 +16,7 @@ import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder import java.time.format.DateTimeFormatterBuilder
import java.time.format.SignStyle import java.time.format.SignStyle
import java.time.temporal.ChronoField.* import java.time.temporal.ChronoField.*
import java.util.*
private val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run { private val ISO_DATE_TIME: DateTimeFormatter = DateTimeFormatterBuilder().run {
parseCaseInsensitive() parseCaseInsensitive()
@ -52,7 +53,7 @@ fun Project.addBenchmarkProperties() {
rootProject.subprojects.forEach { p -> rootProject.subprojects.forEach { p ->
p.extensions.findByType(KScienceReadmeExtension::class.java)?.run { p.extensions.findByType(KScienceReadmeExtension::class.java)?.run {
benchmarksProject.extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg -> benchmarksProject.extensions.findByType(BenchmarksExtension::class.java)?.configurations?.forEach { cfg ->
property("benchmark${cfg.name.capitalize()}") { property("benchmark${cfg.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}") {
val launches = benchmarksProject.buildDir.resolve("reports/benchmarks/${cfg.name}") val launches = benchmarksProject.buildDir.resolve("reports/benchmarks/${cfg.name}")
val resDirectory = launches.listFiles()?.maxByOrNull { val resDirectory = launches.listFiles()?.maxByOrNull {

@ -58,10 +58,10 @@ dependencies {
kotlin { kotlin {
jvmToolchain(11) jvmToolchain(11)
sourceSets.all { sourceSets.all {
with(languageSettings) { languageSettings {
optIn("kotlin.contracts.ExperimentalContracts") optIn("kotlin.contracts.ExperimentalContracts")
optIn("kotlin.ExperimentalUnsignedTypes") optIn("kotlin.ExperimentalUnsignedTypes")
optIn("space.kscience.kmath.misc.UnstableKMathAPI") optIn("space.kscience.kmath.UnstableKMathAPI")
} }
} }
} }

@ -0,0 +1,91 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.expressions
import space.kscience.kmath.UnstableKMathAPI
// Only kmath-core is needed.
// Let's declare some variables
val x by symbol
val y by symbol
val z by symbol
@OptIn(UnstableKMathAPI::class)
fun main() {
// Let's define some random expression.
val someExpression = Double.autodiff.differentiate {
// We bind variables `x` and `y` to the builder scope,
val x = bindSymbol(x)
val y = bindSymbol(y)
// Then we use the bindings to define expression `xy + x + y - 1`
x * y + x + y - 1
}
// Then we can evaluate it at any point ((-1, -1) in the case):
println(someExpression(x to -1.0, y to -1.0))
// >>> -2.0
// We can also construct its partial derivatives:
val dxExpression = someExpression.derivative(x) // ∂/∂x. Must be `y+1`
val dyExpression = someExpression.derivative(y) // ∂/∂y. Must be `x+1`
val dxdxExpression = someExpression.derivative(x, x) // ∂^2/∂x^2. Must be `0`
// We can evaluate them as well
println(dxExpression(x to 57.0, y to 6.0))
// >>> 7.0
println(dyExpression(x to -1.0, y to 179.0))
// >>> 0.0
println(dxdxExpression(x to 239.0, y to 30.0))
// >>> 0.0
// You can also provide extra arguments that obviously won't affect the result:
println(dxExpression(x to 57.0, y to 6.0, z to 42.0))
// >>> 7.0
println(dyExpression(x to -1.0, y to 179.0, z to 0.0))
// >>> 0.0
println(dxdxExpression(x to 239.0, y to 30.0, z to 100_000.0))
// >>> 0.0
// But in case you forgot to specify bound symbol's value, exception is thrown:
println( runCatching { someExpression(z to 4.0) } )
// >>> Failure(java.lang.IllegalStateException: Symbol 'x' is not supported in ...)
// The reason is that the expression is evaluated lazily,
// and each `bindSymbol` operation actually substitutes the provided symbol with the corresponding value.
// For example, let there be an expression
val simpleExpression = Double.autodiff.differentiate {
val x = bindSymbol(x)
x pow 2
}
// When you evaluate it via
simpleExpression(x to 1.0, y to 57.0, z to 179.0)
// lambda above has the context of map `{x: 1.0, y: 57.0, z: 179.0}`.
// When x is bound, you can think of it as substitution `x -> 1.0`.
// Other values are unused which does not make any problem to us.
// But in the case the corresponding value is not provided,
// we cannot bind the variable. Thus, exception is thrown.
// There is also a function `bindSymbolOrNull` that fixes the problem:
val fixedExpression = Double.autodiff.differentiate {
val x = bindSymbolOrNull(x) ?: const(8.0)
x pow -2
}
println(fixedExpression())
// >>> 0.015625
// It works!
// The expression provides a bunch of operations:
// 1. Constant bindings (via `const` and `number`).
// 2. Variable bindings (via `bindVariable`, `bindVariableOrNull`).
// 3. Arithmetic operations (via `+`, `-`, `*`, and `-`).
// 4. Exponentiation (via `pow` or `power`).
// 5. `exp` and `ln`.
// 6. Trigonometrical functions (`sin`, `cos`, `tan`, `cot`).
// 7. Inverse trigonometrical functions (`asin`, `acos`, `atan`, `acot`).
// 8. Hyperbolic functions and inverse hyperbolic functions.
}

@ -9,7 +9,7 @@ kotlin.native.ignoreDisabledTargets=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m org.gradle.jvmargs=-Xmx4096m
toolsVersion=0.14.6-kotlin-1.8.20 toolsVersion=0.14.8-kotlin-1.8.20
org.gradle.parallel=true org.gradle.parallel=true

@ -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-8.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -3,6 +3,8 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI

@ -5,6 +5,7 @@
package space.kscience.kmath.ast package space.kscience.kmath.ast
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.MST
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
@ -30,6 +31,7 @@ private object GenericAsmCompilerTestContext : CompilerTestContext {
asmCompile(algebra as Algebra<Double>, arguments) asmCompile(algebra as Algebra<Double>, arguments)
} }
@OptIn(UnstableKMathAPI::class)
private object PrimitiveAsmCompilerTestContext : CompilerTestContext { private object PrimitiveAsmCompilerTestContext : CompilerTestContext {
override fun MST.compileToExpression(algebra: IntRing): Expression<Int> = asmCompileToExpression(algebra) override fun MST.compileToExpression(algebra: IntRing): Expression<Int> = asmCompileToExpression(algebra)
override fun MST.compile(algebra: IntRing, arguments: Map<Symbol, Int>): Int = asmCompile(algebra, arguments) override fun MST.compile(algebra: IntRing, arguments: Map<Symbol, Int>): Int = asmCompile(algebra, arguments)

@ -7,23 +7,7 @@ kscience {
js() js()
native() native()
wasm{ wasm()
browser {
testTask {
useKarma {
this.webpackConfig.experiments.add("topLevelAwait")
useChromeHeadless()
useConfigDirectory(project.projectDir.resolve("karma.config.d").resolve("wasm"))
}
}
}
}
wasmTest{
dependencies {
implementation(kotlin("test"))
}
}
dependencies { dependencies {
api(projects.kmathCore) api(projects.kmathCore)

File diff suppressed because it is too large Load Diff

@ -6,23 +6,7 @@ kscience{
jvm() jvm()
js() js()
native() native()
wasm{ wasm()
browser {
testTask {
useKarma {
this.webpackConfig.experiments.add("topLevelAwait")
useChromeHeadless()
useConfigDirectory(project.projectDir.resolve("karma.config.d").resolve("wasm"))
}
}
}
}
wasmTest{
dependencies {
implementation(kotlin("test"))
}
}
dependencies { dependencies {
api(projects.kmathMemory) api(projects.kmathMemory)

@ -9,7 +9,7 @@ package space.kscience.kmath.misc
* The same as [zipWithNext], but includes link between last and first element * The same as [zipWithNext], but includes link between last and first element
*/ */
public inline fun <T, R> List<T>.zipWithNextCircular(transform: (a: T, b: T) -> R): List<R> { public inline fun <T, R> List<T>.zipWithNextCircular(transform: (a: T, b: T) -> R): List<R> {
if (isEmpty()) return emptyList() if (size < 2) return emptyList()
return indices.map { i -> return indices.map { i ->
if (i == size - 1) { if (i == size - 1) {
transform(last(), first()) transform(last(), first())
@ -19,4 +19,4 @@ public inline fun <T, R> List<T>.zipWithNextCircular(transform: (a: T, b: T) ->
} }
} }
public fun <T> List<T>.zipWithNextCircular(): List<Pair<T,T>> = zipWithNextCircular { l, r -> l to r } public fun <T> List<T>.zipWithNextCircular(): List<Pair<T, T>> = zipWithNextCircular { l, r -> l to r }

@ -5,7 +5,6 @@ import space.kscience.kmath.UnstableKMathAPI
/** /**
* Non-boxing access to primitive [Double] * Non-boxing access to primitive [Double]
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) { public fun Buffer<Double>.getDouble(index: Int): Double = if (this is BufferView) {
val originIndex = originIndex(index) val originIndex = originIndex(index)

@ -7,6 +7,7 @@
package space.kscience.kmath.streaming package space.kscience.kmath.streaming
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
@ -25,6 +26,7 @@ public fun <T> Buffer<T>.asFlow(): Flow<T> = iterator().asFlow()
/** /**
* Flat map a [Flow] of [Buffer] into continuous [Flow] of elements * Flat map a [Flow] of [Buffer] into continuous [Flow] of elements
*/ */
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T> Flow<Buffer<T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() } public fun <T> Flow<Buffer<T>>.spread(): Flow<T> = flatMapConcat { it.asFlow() }
/** /**

@ -7,23 +7,9 @@ kscience{
js() js()
native() native()
wasm{ wasm()
browser {
testTask {
useKarma {
this.webpackConfig.experiments.add("topLevelAwait")
useChromeHeadless()
useConfigDirectory(project.projectDir.resolve("karma.config.d").resolve("wasm"))
}
}
}
}
wasmTest{
dependencies {
implementation(kotlin("test"))
}
}
dependencies { dependencies {
api(projects.kmathCore) api(projects.kmathCore)

@ -44,7 +44,9 @@ class RotationTest {
@Test @Test
fun fromEuler() { fun fromEuler() {
val q = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.ZXY) val q = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.ZXY)
assertBufferEquals(DoubleBuffer(0.9818562, 0.0342708, 0.1060205, 0.1534393), q) assertBufferEquals(DoubleBuffer(0.9818562, 0.0342708, 0.1060205, 0.1534393), q)
val q1 = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.XYZ)
assertBufferEquals(DoubleBuffer(0.9818562, 0.0640713, 0.0911575, 0.1534393), q1)
} }
} }

@ -20,8 +20,4 @@ readme {
feature("jafama-double", "src/main/kotlin/space/kscience/kmath/jafama/") { feature("jafama-double", "src/main/kotlin/space/kscience/kmath/jafama/") {
"Double ExtendedField implementations based on Jafama" "Double ExtendedField implementations based on Jafama"
} }
} }
kotlin.sourceSets.all {
languageSettings.optIn("space.kscience.kmath.misc.UnstableKMathAPI")
}

@ -14,10 +14,6 @@ readme {
maturity = space.kscience.gradle.Maturity.PROTOTYPE maturity = space.kscience.gradle.Maturity.PROTOTYPE
} }
kotlin.sourceSets.all {
languageSettings.optIn("space.kscience.kmath.misc.UnstableKMathAPI")
}
tasks.processJupyterApiResources { tasks.processJupyterApiResources {
libraryProducers = listOf("space.kscience.kmath.jupyter.KMathJupyter") libraryProducers = listOf("space.kscience.kmath.jupyter.KMathJupyter")
} }

@ -5,7 +5,7 @@ plugins {
kotlin.sourceSets kotlin.sourceSets
.filter { it.name.contains("test", true) } .filter { it.name.contains("test", true) }
.map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings) .map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings)
.forEach { it.optIn("space.kscience.kmath.misc.UnstableKMathAPI") } .forEach { it.optIn("space.kscience.kmath.UnstableKMathAPI") }
description = "Kotlin∇ integration module" description = "Kotlin∇ integration module"

@ -6,6 +6,7 @@
package space.kscience.kmath.kotlingrad package space.kscience.kmath.kotlingrad
import ai.hypergraph.kotlingrad.api.* import ai.hypergraph.kotlingrad.api.*
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.asm.compileToExpression
import space.kscience.kmath.ast.parseMath import space.kscience.kmath.ast.parseMath
import space.kscience.kmath.expressions.MstNumericAlgebra import space.kscience.kmath.expressions.MstNumericAlgebra
@ -17,6 +18,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
import kotlin.test.fail import kotlin.test.fail
@OptIn(UnstableKMathAPI::class)
internal class AdaptingTests { internal class AdaptingTests {
@Test @Test
fun symbol() { fun symbol() {

@ -1,3 +1,14 @@
public abstract interface annotation class space/kscience/kmath/PerformancePitfall : java/lang/annotation/Annotation {
public abstract fun message ()Ljava/lang/String;
}
public abstract interface annotation class space/kscience/kmath/UnsafeKMathAPI : java/lang/annotation/Annotation {
public abstract fun message ()Ljava/lang/String;
}
public abstract interface annotation class space/kscience/kmath/UnstableKMathAPI : java/lang/annotation/Annotation {
}
public final class space/kscience/kmath/memory/ByteBufferMemory : space/kscience/kmath/memory/Memory { public final class space/kscience/kmath/memory/ByteBufferMemory : space/kscience/kmath/memory/Memory {
public fun <init> (Ljava/nio/ByteBuffer;II)V public fun <init> (Ljava/nio/ByteBuffer;II)V
public synthetic fun <init> (Ljava/nio/ByteBuffer;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Ljava/nio/ByteBuffer;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V
@ -36,7 +47,8 @@ public final class space/kscience/kmath/memory/MemoryKt {
public static final fun write (Lspace/kscience/kmath/memory/Memory;Lkotlin/jvm/functions/Function1;)V public static final fun write (Lspace/kscience/kmath/memory/Memory;Lkotlin/jvm/functions/Function1;)V
} }
public abstract interface class space/kscience/kmath/memory/MemoryReader { public abstract interface class space/kscience/kmath/memory/MemoryReader : java/lang/AutoCloseable {
public abstract fun close ()V
public abstract fun getMemory ()Lspace/kscience/kmath/memory/Memory; public abstract fun getMemory ()Lspace/kscience/kmath/memory/Memory;
public abstract fun readByte (I)B public abstract fun readByte (I)B
public abstract fun readDouble (I)D public abstract fun readDouble (I)D
@ -44,7 +56,6 @@ public abstract interface class space/kscience/kmath/memory/MemoryReader {
public abstract fun readInt (I)I public abstract fun readInt (I)I
public abstract fun readLong (I)J public abstract fun readLong (I)J
public abstract fun readShort (I)S public abstract fun readShort (I)S
public abstract fun release ()V
} }
public abstract interface class space/kscience/kmath/memory/MemorySpec { public abstract interface class space/kscience/kmath/memory/MemorySpec {
@ -59,9 +70,9 @@ public final class space/kscience/kmath/memory/MemorySpecKt {
public static final fun writeArray (Lspace/kscience/kmath/memory/MemoryWriter;Lspace/kscience/kmath/memory/MemorySpec;I[Ljava/lang/Object;)V public static final fun writeArray (Lspace/kscience/kmath/memory/MemoryWriter;Lspace/kscience/kmath/memory/MemorySpec;I[Ljava/lang/Object;)V
} }
public abstract interface class space/kscience/kmath/memory/MemoryWriter { public abstract interface class space/kscience/kmath/memory/MemoryWriter : java/lang/AutoCloseable {
public abstract fun close ()V
public abstract fun getMemory ()Lspace/kscience/kmath/memory/Memory; public abstract fun getMemory ()Lspace/kscience/kmath/memory/Memory;
public abstract fun release ()V
public abstract fun writeByte (IB)V public abstract fun writeByte (IB)V
public abstract fun writeDouble (ID)V public abstract fun writeDouble (ID)V
public abstract fun writeFloat (IF)V public abstract fun writeFloat (IF)V

@ -6,23 +6,7 @@ kscience {
jvm() jvm()
js() js()
native() native()
wasm{ wasm()
browser {
testTask {
useKarma {
this.webpackConfig.experiments.add("topLevelAwait")
useChromeHeadless()
useConfigDirectory(project.projectDir.resolve("karma.config.d").resolve("wasm"))
}
}
}
}
wasmTest{
dependencies {
implementation(kotlin("test"))
}
}
} }
readme { readme {

@ -9,9 +9,6 @@ kscience{
} }
kotlin.sourceSets { kotlin.sourceSets {
all {
languageSettings.optIn("space.kscience.kmath.misc.UnstableKMathAPI")
}
commonMain { commonMain {
dependencies { dependencies {

@ -6,6 +6,7 @@
package space.kscience.kmath.distributions package space.kscience.kmath.distributions
import space.kscience.kmath.chains.Chain import space.kscience.kmath.chains.Chain
import space.kscience.kmath.operations.DoubleField.pow
import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.GaussianSampler
import space.kscience.kmath.samplers.InternalErf import space.kscience.kmath.samplers.InternalErf
@ -34,8 +35,23 @@ public class NormalDistribution(public val sampler: GaussianSampler) : Distribut
} }
} }
private companion object { public companion object {
private val SQRT2 = sqrt(2.0) private val SQRT2 = sqrt(2.0)
/**
* Zelen & Severo approximation for the standard normal CDF.
* The error upper boundary by 7.5 * 10e-8.
*/
public fun zSNormalCDF(x: Double): Double {
val t = 1 / (1 + 0.2316419 * abs(x))
val sum = 0.319381530 * t -
0.356563782 * t.pow(2) +
1.781477937 * t.pow(3) -
1.821255978 * t.pow(4) +
1.330274429 * t.pow(5)
val temp = sum * exp(-abs(x).pow(2) / 2) / (2 * PI).pow(0.5)
return if (x >= 0) 1 - temp else temp
}
} }
} }

@ -191,7 +191,7 @@ public open class SeriesAlgebra<T, out A : Ring<T>, out BA : BufferAlgebra<T, A>
crossinline operation: A.(left: T, right: T) -> T, crossinline operation: A.(left: T, right: T) -> T,
): Series<T> { ): Series<T> {
val newRange = offsetIndices.intersect(other.offsetIndices) val newRange = offsetIndices.intersect(other.offsetIndices)
return seriesByOffset(startOffset = newRange.first, size = newRange.last - newRange.first) { offset -> return seriesByOffset(startOffset = newRange.first, size = newRange.last + 1 - newRange.first) { offset ->
elementAlgebra.operation( elementAlgebra.operation(
getByOffset(offset), getByOffset(offset),
other.getByOffset(offset) other.getByOffset(offset)
@ -199,12 +199,25 @@ public open class SeriesAlgebra<T, out A : Ring<T>, out BA : BufferAlgebra<T, A>
} }
} }
/**
* Zip buffer with itself, but shifted
* */
public inline fun Buffer<T>.zipWithShift(
shift: Int = 1,
crossinline operation: A.(left: T, right: T) -> T
): Buffer<T> {
val shifted = this.moveBy(shift)
return zip(shifted, operation)
}
override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it } override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
override fun add(left: Buffer<T>, right: Buffer<T>): Series<T> = left.zip(right) { l, r -> l + r } override fun add(left: Buffer<T>, right: Buffer<T>): Series<T> = left.zip(right) { l, r -> l + r }
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = left.zip(right) { l, r -> l * r } override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = left.zip(right) { l, r -> l * r }
public fun Buffer<T>.difference(shift: Int=1): Buffer<T> = this.zipWithShift(shift) {l, r -> r - l}
public companion object public companion object
} }

@ -0,0 +1,82 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.series
import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.operations.DoubleField.pow
import space.kscience.kmath.operations.fold
import kotlin.math.absoluteValue
/**
* Container class for Variance Ratio Test result:
* ratio itself, corresponding Z-score, also it's p-value
*/
public data class VarianceRatioTestResult(
val varianceRatio: Double = 1.0,
val zScore: Double = 0.0,
val pValue: Double = 0.5,
)
/**
* Calculates the Z-statistic and the p-value for the Lo and MacKinlay's Variance Ratio test (1987)
* under Homoscedastic or Heteroscedstic assumptions
* with two-sided p-value test
* https://ssrn.com/abstract=346975
*
* @author https://github.com/mrFendel
*/
public fun SeriesAlgebra<Double, *, *, *>.varianceRatioTest(
series: Series<Double>,
shift: Int,
homoscedastic: Boolean = true,
): VarianceRatioTestResult {
require(shift > 1) { "Shift must be greater than one" }
require(shift < series.size) { "Shift must be smaller than sample size" }
val sum = { x: Double, y: Double -> x + y }
val mean = series.fold(0.0, sum) / series.size
val demeanedSquares = series.map { (it - mean).pow(2) }
val variance = demeanedSquares.fold(0.0, sum)
if (variance == 0.0) return VarianceRatioTestResult()
var seriesAgg = series
for (i in 1..<shift) {
seriesAgg = seriesAgg.zip(series.moveTo(i)) { v1, v2 -> v1 + v2 }
}
val demeanedSquaresAgg = seriesAgg.map { (it - shift * mean).pow(2) }
val varianceAgg = demeanedSquaresAgg.fold(0.0, sum)
val varianceRatio =
varianceAgg * (series.size.toDouble() - 1) / variance / (series.size.toDouble() - shift.toDouble() + 1) / (1 - shift.toDouble() / series.size.toDouble()) / shift.toDouble()
// calculating asymptotic variance
val phi = if (homoscedastic) { // under homoscedastic null hypothesis
2 * (2 * shift - 1.0) * (shift - 1.0) / (3 * shift * series.size)
} else { // under heteroscedastic null hypothesis
var accumulator = 0.0
for (j in 1..<shift) {
val temp = demeanedSquares
val delta = series.size * temp.zipWithShift(j) { v1, v2 -> v1 * v2 }.fold(0.0, sum) / variance.pow(2)
accumulator += delta * 4 * (shift - j).toDouble().pow(2) / shift.toDouble().pow(2)
}
accumulator
}
val zScore = (varianceRatio - 1) / phi.pow(0.5)
val pValue = 2 * (1 - NormalDistribution.zSNormalCDF(zScore.absoluteValue))
return VarianceRatioTestResult(varianceRatio, zScore, pValue)
}

@ -0,0 +1,72 @@
/*
* Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.series
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.bufferAlgebra
import kotlin.math.PI
import kotlin.test.Test
import kotlin.test.assertEquals
class TestVarianceRatioTest {
@Test
fun monotonicData() {
with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
val monotonicData = series(10) { it * 1.0 }
val resultHomo = varianceRatioTest(monotonicData, 2, homoscedastic = true)
assertEquals(1.818181, resultHomo.varianceRatio, 1e-6)
// homoscedastic zScore
assertEquals(2.587318, resultHomo.zScore, 1e-6)
assertEquals(.0096, resultHomo.pValue, 1e-4)
val resultHetero = varianceRatioTest(monotonicData, 2, homoscedastic = false)
// heteroscedastic zScore
assertEquals(0.819424, resultHetero.zScore, 1e-6)
assertEquals(.4125, resultHetero.pValue, 1e-4)
}
}
@Test
fun volatileData() {
with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
val volatileData = series(10) { sin(PI * it + PI/2) + 1.0}
val resultHomo = varianceRatioTest(volatileData, 2)
assertEquals(0.0, resultHomo.varianceRatio, 1e-6)
// homoscedastic zScore
assertEquals(-3.162277, resultHomo.zScore, 1e-6)
assertEquals(.0015, resultHomo.pValue, 1e-4)
val resultHetero = varianceRatioTest(volatileData, 2, homoscedastic = false)
// heteroscedastic zScore
assertEquals(-1.0540925, resultHetero.zScore, 1e-6)
assertEquals(.2918, resultHetero.pValue, 1e-4)
}
}
@Test
fun negativeData() {
with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
val negativeData = series(10) { sin(it * 1.2)}
val resultHomo = varianceRatioTest(negativeData, 3)
assertEquals(1.240031, resultHomo.varianceRatio, 1e-6)
// homoscedastic zScore
assertEquals(0.509183, resultHomo.zScore, 1e-6)
val resultHetero = varianceRatioTest(negativeData, 3, homoscedastic = false)
// heteroscedastic zScore
assertEquals(0.209202, resultHetero.zScore, 1e-6)
}
}
@Test
fun zeroVolatility() {
with(Double.algebra.bufferAlgebra.seriesAlgebra()) {
val zeroVolData = series(10) { 0.0 }
val result = varianceRatioTest(zeroVolData, 4)
assertEquals(1.0, result.varianceRatio, 1e-6)
assertEquals(0.0, result.zScore, 1e-6)
assertEquals(0.5, result.pValue, 1e-4)
}
}
}

@ -14,13 +14,6 @@ kscience{
} }
kotlin.sourceSets { kotlin.sourceSets {
all {
languageSettings.optIn("space.kscience.kmath.UnstableKMathAPI")
}
filter { it.name.contains("test", true) }
.map(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::languageSettings)
.forEach { it.optIn("space.kscience.kmath.PerformancePitfall") }
commonMain { commonMain {
dependencies { dependencies {

@ -29,7 +29,7 @@ public class space/kscience/kmath/viktor/ViktorFieldND : space/kscience/kmath/vi
public synthetic fun getOne ()Ljava/lang/Object; public synthetic fun getOne ()Ljava/lang/Object;
public synthetic fun getOne ()Lspace/kscience/kmath/nd/StructureND; public synthetic fun getOne ()Lspace/kscience/kmath/nd/StructureND;
public fun getOne ()Lspace/kscience/kmath/viktor/ViktorStructureND; public fun getOne ()Lspace/kscience/kmath/viktor/ViktorStructureND;
public fun getShape ()[I public fun getShape-IIYLAfE ()[I
public synthetic fun getZero ()Ljava/lang/Object; public synthetic fun getZero ()Ljava/lang/Object;
public synthetic fun getZero ()Lspace/kscience/kmath/nd/StructureND; public synthetic fun getZero ()Lspace/kscience/kmath/nd/StructureND;
public fun getZero ()Lspace/kscience/kmath/viktor/ViktorStructureND; public fun getZero ()Lspace/kscience/kmath/viktor/ViktorStructureND;
@ -85,8 +85,8 @@ public class space/kscience/kmath/viktor/ViktorFieldOpsND : space/kscience/kmath
public fun sin (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND; public fun sin (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun sinh (Ljava/lang/Object;)Ljava/lang/Object; public synthetic fun sinh (Ljava/lang/Object;)Ljava/lang/Object;
public fun sinh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND; public fun sinh (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun structureND ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/nd/StructureND; public synthetic fun structureND-qL90JFI ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/nd/StructureND;
public fun structureND ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/viktor/ViktorStructureND; public fun structureND-qL90JFI ([ILkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun tan (Ljava/lang/Object;)Ljava/lang/Object; public synthetic fun tan (Ljava/lang/Object;)Ljava/lang/Object;
public fun tan (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND; public fun tan (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/viktor/ViktorStructureND;
public synthetic fun times (Ljava/lang/Object;Ljava/lang/Number;)Ljava/lang/Object; public synthetic fun times (Ljava/lang/Object;Ljava/lang/Number;)Ljava/lang/Object;
@ -112,7 +112,7 @@ public final class space/kscience/kmath/viktor/ViktorStructureND : space/kscienc
public fun get ([I)Ljava/lang/Double; public fun get ([I)Ljava/lang/Double;
public synthetic fun get ([I)Ljava/lang/Object; public synthetic fun get ([I)Ljava/lang/Object;
public final fun getF64Buffer ()Lorg/jetbrains/bio/viktor/F64Array; public final fun getF64Buffer ()Lorg/jetbrains/bio/viktor/F64Array;
public fun getShape ()[I public fun getShape-IIYLAfE ()[I
public fun set ([ID)V public fun set ([ID)V
public synthetic fun set ([ILjava/lang/Object;)V public synthetic fun set ([ILjava/lang/Object;)V
} }

4
test-utils/README.md Normal file

@ -0,0 +1,4 @@
# Module test-utils

@ -0,0 +1,32 @@
public final class space/kscience/kmath/testutils/AssertsKt {
public static final fun assertBufferEquals (Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/structures/Buffer;D)V
public static synthetic fun assertBufferEquals$default (Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/structures/Buffer;DILjava/lang/Object;)V
}
public final class space/kscience/kmath/testutils/BufferEqualityKt {
public static final fun contentEquals-2c9zdjM ([D[D)Z
public static final fun contentEqualsArray ([D[D)Z
public static final fun contentEqualsBuffer ([D[D)Z
}
public final class space/kscience/kmath/testutils/FieldVerifier : space/kscience/kmath/testutils/RingVerifier {
public fun <init> (Lspace/kscience/kmath/operations/Field;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Number;)V
public fun verify ()V
}
public class space/kscience/kmath/testutils/RingVerifier : space/kscience/kmath/testutils/SpaceVerifier {
public fun <init> (Lspace/kscience/kmath/operations/Ring;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Number;)V
public fun verify ()V
}
public class space/kscience/kmath/testutils/SpaceVerifier : space/kscience/kmath/testutils/AlgebraicVerifier {
public fun <init> (Lspace/kscience/kmath/operations/Ring;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Number;)V
public final fun getA ()Ljava/lang/Object;
public synthetic fun getAlgebra ()Lspace/kscience/kmath/operations/Algebra;
public fun getAlgebra ()Lspace/kscience/kmath/operations/Ring;
public final fun getB ()Ljava/lang/Object;
public final fun getC ()Ljava/lang/Object;
public final fun getX ()Ljava/lang/Number;
public fun verify ()V
}