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

1
.gitignore vendored
View File

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

View File

@ -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\"",
)
}
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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