Dev #49

Merged
altavir merged 5 commits from dev into master 2019-02-22 13:53:11 +03:00
19 changed files with 356 additions and 87 deletions

2
.gitignore vendored
View File

@ -8,4 +8,4 @@
# Cache of project # Cache of project
.gradletasknamecache .gradletasknamecache
artifactory.gradle gradle.properties

View File

@ -1,27 +1,48 @@
Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/scientifik/scientifik.kmath/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/scientifik.kmath/_latestVersion)
# KMath # KMath
The Kotlin MATHematics library is intended as a Kotlin-based analog to Python's `numpy` library. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. The Kotlin MATHematics library is intended as a Kotlin-based analog to Python's `numpy` library. In contrast to `numpy` and `scipy` it is modular and has a lightweight core.
## Features ## Features
Actual feature list is [here](doc/features.md)
* **Algebra** * **Algebra**
* Algebraic structures like rings, spaces and field (**TODO** add example to wiki) * Algebraic structures like rings, spaces and field (**TODO** add example to wiki)
* Basic linear algebra operations (sums, products, etc.), backed by the `Space` API. * Basic linear algebra operations (sums, products, etc.), backed by the `Space` API.
* Complex numbers backed by the `Field` API (meaning that they will be usable in any structure like vectors and N-dimensional arrays). * Complex numbers backed by the `Field` API (meaning that they will be usable in any structure like vectors and N-dimensional arrays).
* [In progress] advanced linear algebra operations like matrix inversion and LU decomposition. * Advanced linear algebra operations like matrix inversion and LU decomposition.
* **Array-like structures** Full support of [numpy-like ndarrays](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.ndarray.html) including mixed arithmetic operations and function operations over arrays and numbers just like in Python (with the added benefit of static type checking).
* **Expressions** Expressions are one of the ultimate goals of KMath. By writing a single mathematical expression * **Array-like structures** Full support of many-dimenstional array-like structures
including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking).
* **Expressions** By writing a single mathematical expression
once, users will be able to apply different types of objects to the expression by providing a context. Exceptions once, users will be able to apply different types of objects to the expression by providing a context. Exceptions
can be used for a wide variety of purposes from high performance calculations to code generation. can be used for a wide variety of purposes from high performance calculations to code generation.
* **Histograms** Fast multi-dimensional histograms.
* **Streaming** Streaming operations on mathematica objects and objects buffers.
* **Commons-math wrapper** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/)
library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free
to submit a feature request if you want something to be done first.
* **Koma wrapper** [Koma](https://github.com/kyonifer/koma) is a well established numerics library in kotlin, specifically linear algebra.
The plan is to have wrappers for koma implementations for compatibility with kmath API.
## Planned features ## Planned features
* **Common mathematics** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/)
library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free
to submit a feature request if you want something to be done first.
* **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks. * **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks.
* **Array statistics**
* **Integration** Univariate and multivariate integration framework.
* **Probability and distributions**
* **Fitting** Non-linear curve fitting facilities
## Multi-platform support ## Multi-platform support
KMath is developed as a multi-platform library, which means that most of interfaces are declared in the [common module](kmath-core/src/commonMain). KMath is developed as a multi-platform library, which means that most of interfaces are declared in the [common module](kmath-core/src/commonMain).
@ -39,6 +60,10 @@ of optimized parts should be better than SciPy.
## Releases ## Releases
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: 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 ```groovy
@ -52,7 +77,7 @@ or for the Gradle Kotlin DSL:
```kotlin ```kotlin
repositories { repositories {
maven { setUrl("http://npm.mipt.ru:8081/artifactory/gradle-dev") } maven("http://npm.mipt.ru:8081/artifactory/gradle-dev")
mavenCentral() mavenCentral()
} }
``` ```
@ -60,16 +85,28 @@ repositories {
Then use a regular dependency like so: Then use a regular dependency like so:
```groovy ```groovy
compile(group: 'scientifik', name: 'kmath-core', version: '0.0.1-SNAPSHOT') api "scientifik:kmath-core-jvm:0.1.0-dev"
``` ```
or in the Gradle Kotlin DSL: or in the Gradle Kotlin DSL:
```kotlin ```kotlin
compile(group = "scientifik", name = "kmath-core", version = "0.0.1-SNAPSHOT") api("scientifik:kmath-core-jvm:0.1.0-dev")
``` ```
Working builds can be obtained here: [![](https://jitpack.io/v/altavir/kmath.svg)](https://jitpack.io/#altavir/kmath). ### Release
Release artifacts are accessible from bintray with following configuration:
```kotlin
repositories{
maven("https://dl.bintray.com/mipt-npm/scientifik")
}
dependencies{
api("scientifik:kmath-core-jvm:0.1.0")
}
```
## Contributing ## Contributing

View File

@ -1,6 +1,5 @@
package scientifik.kmath.structures package scientifik.kmath.structures
import scientifik.kmath.structures.Buffer.Companion.DoubleBufferFactory
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -8,7 +7,7 @@ fun main(args: Array<String>) {
val n = 6000 val n = 6000
val structure = NDStructure.build(intArrayOf(n, n), DoubleBufferFactory) { 1.0 } val structure = NDStructure.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 }
structure.mapToBuffer { it + 1 } // warm-up structure.mapToBuffer { it + 1 } // warm-up

View File

@ -5,6 +5,7 @@ buildscript {
val ioVersion: String by rootProject.extra("0.1.5") val ioVersion: String by rootProject.extra("0.1.5")
val coroutinesVersion: String by rootProject.extra("1.1.1") val coroutinesVersion: String by rootProject.extra("1.1.1")
val atomicfuVersion: String by rootProject.extra("0.12.1") val atomicfuVersion: String by rootProject.extra("0.12.1")
val dokkaVersion: String by rootProject.extra("0.9.17")
repositories { repositories {
//maven("https://dl.bintray.com/kotlin/kotlin-eap") //maven("https://dl.bintray.com/kotlin/kotlin-eap")
@ -14,6 +15,8 @@ buildscript {
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4+") classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4+")
classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion")
} }
} }
@ -21,20 +24,31 @@ plugins {
id("com.jfrog.artifactory") version "4.9.1" apply false id("com.jfrog.artifactory") version "4.9.1" apply false
} }
allprojects { val kmathVersion by extra("0.1.0")
if (project.name.startsWith("kmath")) {
apply(plugin = "maven-publish")
apply(plugin = "com.jfrog.artifactory")
}
allprojects {
group = "scientifik" group = "scientifik"
version = "0.0.3-dev" version = kmathVersion
repositories { repositories {
//maven("https://dl.bintray.com/kotlin/kotlin-eap") //maven("https://dl.bintray.com/kotlin/kotlin-eap")
jcenter() jcenter()
} }
apply(plugin = "maven")
apply(plugin = "maven-publish")
// apply bintray configuration
apply(from = "${rootProject.rootDir}/gradle/bintray.gradle")
//apply artifactory configuration
apply(from = "${rootProject.rootDir}/gradle/artifactory.gradle")
}
subprojects {
if (!name.startsWith("kmath")) return@subprojects
extensions.findByType<KotlinMultiplatformExtension>()?.apply { extensions.findByType<KotlinMultiplatformExtension>()?.apply {
jvm { jvm {
compilations.all { compilations.all {
@ -48,9 +62,49 @@ allprojects {
languageSettings.progressiveMode = true languageSettings.progressiveMode = true
} }
} }
extensions.findByType<PublishingExtension>()?.apply {
publications.filterIsInstance<MavenPublication>().forEach { publication ->
if (publication.name == "kotlinMultiplatform") {
// for our root metadata publication, set artifactId with a package and project name
publication.artifactId = project.name
} else {
// for targets, set artifactId with a package, project name and target name (e.g. iosX64)
publication.artifactId = "${project.name}-${publication.name}"
}
}
// Create empty jar for sources classifier to satisfy maven requirements
val stubSources by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
//from(sourceSets.main.get().allSource)
}
// Create empty jar for javadoc classifier to satisfy maven requirements
val stubJavadoc by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
}
extensions.findByType<KotlinMultiplatformExtension>()?.apply {
targets.forEach { target ->
val publication = publications.findByName(target.name) as MavenPublication
// Patch publications with fake javadoc
publication.artifact(stubJavadoc)
// Remove gradle metadata publishing from all targets which are not native
// if (target.platformType.name != "native") {
// publication.gradleModuleMetadataFile = null
// tasks.matching { it.name == "generateMetadataFileFor${name.capitalize()}Publication" }.all {
// onlyIf { false }
// }
// }
}
}
}
} }
} }
if (file("artifactory.gradle").exists()) {
apply(from = "artifactory.gradle")
}

View File

@ -68,3 +68,44 @@ One important distinction between algebra elements and algebra contexts is that
The middle type is needed in case algebra members do not store context. For example, it is not possible to add The middle type is needed in case algebra members do not store context. For example, it is not possible to add
a context to regular `Double`. The element performs automatic conversions from context types and back. a context to regular `Double`. The element performs automatic conversions from context types and back.
One should used context operations in all important places. The performance of element operations is not guaranteed. One should used context operations in all important places. The performance of element operations is not guaranteed.
## Spaces and fields
An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces,
rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object
stores a reference to the `Field` which contains additive and multiplicative operations, meaning
it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context:
```kotlin
val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.i
val c3 = c1 + c2
```
`ComplexField` also features special operations to mix complex and real numbers, for example:
```kotlin
val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0]
val c3 = ComplexField.run{ c1 - i*2.0}
```
**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
## Nested fields
Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so:
```kotlin
val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray ->
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble())
}
```
The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own
`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex
numbers and implement operations on it, one just needs to provide a field for its elements.
**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like
`MemorySpec`.

View File

@ -1 +1,15 @@
**TODO** # Buffers
Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`).
There are different types of buffers:
* Primitive buffers wrapping like `DoubleBuffer` which are wrapping primitive arrays.
* Boxing `ListBuffer` wrapping a list
* Functionally defined `VirtualBuffer` which does not hold a state itself, but provides a function to calculate value
* `MemoryBuffer` allows direct allocation of objects in continuous memory block.
Some kmath features require a `BufferFactory` class to operate properly. A general convention is to use functions defined in
`Buffer` and `MutableBuffer` companion classes. For example factory `Buffer.Companion::auto` in most cases creates the most suitable
buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory).
## Buffer performance
One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead

View File

@ -0,0 +1,17 @@
# Features
* [Algebra](./algebra.md) - [Context-based](./contexts.md) operations on different primitives and structures.
* [NDStructures](./nd-structure.md)
* [Linear algebra](linear) - Matrices, operations and linear equations solving. To be moved to separate module. Currently supports basic
api and multiple library back-ends.
* [Histograms](./histograms.md) - Multidimensional histogram calculation and operations.
* [Expressions](./expressions.md)
* Commons math integration
* Koma integration

View File

@ -1,3 +1,7 @@
# Nd-structure generation and operations
**TODO**
# Performance for n-dimensional structures operations # Performance for n-dimensional structures operations
One of the most sought after features of mathematical libraries is the high-performance operations on n-dimensional One of the most sought after features of mathematical libraries is the high-performance operations on n-dimensional

View File

@ -1,40 +0,0 @@
## Spaces and fields
An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces,
rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object
stores a reference to the `Field` which contains additive and multiplicative operations, meaning
it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context:
```kotlin
val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.i
val c3 = c1 + c2
```
`ComplexField` also features special operations to mix complex and real numbers, for example:
```kotlin
val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0]
val c3 = ComplexField.run{ c1 - i*2.0}
```
**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
## Nested fields
Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so:
```kotlin
val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray ->
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble())
}
```
The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own
`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex
numbers and implement operations on it, one just needs to provide a field for its elements.
**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like
`BufferSpec`. This feature is in development phase.

31
gradle/artifactory.gradle Normal file
View File

@ -0,0 +1,31 @@
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
}
}
}

85
gradle/bintray.gradle Normal file
View File

@ -0,0 +1,85 @@
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
}
}

View File

@ -1,5 +1,6 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
`maven-publish`
} }
description = "Commons math binding for kmath" description = "Commons math binding for kmath"
@ -11,3 +12,18 @@ dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit") testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
} }
val sourcesJar by tasks.registering(Jar::class) {
classifier = "sources"
from(sourceSets.main.get().allSource)
}
publishing {
publications {
register("jvm", MavenPublication::class) {
from(components["java"])
artifact(sourcesJar.get())
}
}
}

View File

@ -2,6 +2,7 @@ package scientifik.kmath.linear
import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Ring
import scientifik.kmath.structures.* import scientifik.kmath.structures.*
import kotlin.jvm.JvmSynthetic
/** /**
* Basic implementation of Matrix space based on [NDStructure] * Basic implementation of Matrix space based on [NDStructure]
@ -32,7 +33,6 @@ class BufferMatrix<T : Any>(
} }
} }
override val shape: IntArray get() = intArrayOf(rowNum, colNum) override val shape: IntArray get() = intArrayOf(rowNum, colNum)
override fun suggestFeature(vararg features: MatrixFeature) = override fun suggestFeature(vararg features: MatrixFeature) =

View File

@ -4,7 +4,6 @@ import scientifik.kmath.operations.RealField
import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Ring
import scientifik.kmath.operations.sum import scientifik.kmath.operations.sum
import scientifik.kmath.structures.* import scientifik.kmath.structures.*
import scientifik.kmath.structures.Buffer.Companion.DoubleBufferFactory
import scientifik.kmath.structures.Buffer.Companion.boxing import scientifik.kmath.structures.Buffer.Companion.boxing
import kotlin.math.sqrt import kotlin.math.sqrt
@ -33,7 +32,7 @@ interface MatrixContext<T : Any> {
/** /**
* Non-boxing double matrix * Non-boxing double matrix
*/ */
val real = BufferMatrixContext(RealField, DoubleBufferFactory) val real = BufferMatrixContext(RealField, Buffer.Companion::auto)
/** /**
* A structured matrix with custom buffer * A structured matrix with custom buffer

View File

@ -42,7 +42,7 @@ interface VectorSpace<T : Any, S : Space<T>> : Space<Point<T>> {
* Non-boxing double vector space * Non-boxing double vector space
*/ */
fun real(size: Int): BufferVectorSpace<Double, RealField> { fun real(size: Int): BufferVectorSpace<Double, RealField> {
return realSpaceCache.getOrPut(size) { BufferVectorSpace(size, RealField, Buffer.DoubleBufferFactory) } return realSpaceCache.getOrPut(size) { BufferVectorSpace(size, RealField, Buffer.Companion::auto) }
} }
/** /**

View File

@ -1,8 +1,8 @@
package scientifik.kmath.operations package scientifik.kmath.operations
import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Buffer
import scientifik.kmath.structures.MemoryBuffer
import scientifik.kmath.structures.MutableBuffer import scientifik.kmath.structures.MutableBuffer
import scientifik.kmath.structures.ObjectBuffer
import scientifik.memory.MemoryReader import scientifik.memory.MemoryReader
import scientifik.memory.MemorySpec import scientifik.memory.MemorySpec
import scientifik.memory.MemoryWriter import scientifik.memory.MemoryWriter
@ -88,10 +88,10 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl
fun Double.toComplex() = Complex(this, 0.0) fun Double.toComplex() = Complex(this, 0.0)
fun Buffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> { inline fun Buffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer<Complex> {
return ObjectBuffer.create(Complex, size, init) return MemoryBuffer.create(Complex, size, init)
} }
fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> { inline fun MutableBuffer.Companion.complex(size: Int, crossinline init: (Int) -> Complex): Buffer<Complex> {
return ObjectBuffer.create(Complex, size, init) return MemoryBuffer.create(Complex, size, init)
} }

View File

@ -1,5 +1,8 @@
package scientifik.kmath.structures package scientifik.kmath.structures
import scientifik.kmath.operations.Complex
import scientifik.kmath.operations.complex
typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T> typealias BufferFactory<T> = (Int, (Int) -> T) -> Buffer<T>
typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T> typealias MutableBufferFactory<T> = (Int, (Int) -> T) -> MutableBuffer<T>
@ -43,21 +46,16 @@ interface Buffer<T> {
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline fun <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> { inline fun <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> {
//TODO add resolution based on Annotation or companion resolution
return when (T::class) { return when (T::class) {
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T> Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer<T> Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer<T>
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T> Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T>
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer<T> Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer<T>
Complex::class -> complex(size) { initializer(it) as Complex } as Buffer<T>
else -> boxing(size, initializer) else -> boxing(size, initializer)
} }
} }
val DoubleBufferFactory: BufferFactory<Double> =
{ size, initializer -> DoubleBuffer(DoubleArray(size, initializer)) }
val ShortBufferFactory: BufferFactory<Short> =
{ size, initializer -> ShortBuffer(ShortArray(size, initializer)) }
val IntBufferFactory: BufferFactory<Int> = { size, initializer -> IntBuffer(IntArray(size, initializer)) }
val LongBufferFactory: BufferFactory<Long> = { size, initializer -> LongBuffer(LongArray(size, initializer)) }
} }
} }

View File

@ -5,7 +5,7 @@ import scientifik.memory.*
/** /**
* A non-boxing buffer based on [ByteBuffer] storage * A non-boxing buffer based on [ByteBuffer] storage
*/ */
open class ObjectBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> { open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> {
override val size: Int get() = memory.size / spec.objectSize override val size: Int get() = memory.size / spec.objectSize
@ -18,14 +18,14 @@ open class ObjectBuffer<T : Any>(protected val memory: Memory, protected val spe
companion object { companion object {
fun <T : Any> create(spec: MemorySpec<T>, size: Int) = fun <T : Any> create(spec: MemorySpec<T>, size: Int) =
ObjectBuffer(Memory.allocate(size * spec.objectSize), spec) MemoryBuffer(Memory.allocate(size * spec.objectSize), spec)
inline fun <T : Any> create( inline fun <T : Any> create(
spec: MemorySpec<T>, spec: MemorySpec<T>,
size: Int, size: Int,
crossinline initializer: (Int) -> T crossinline initializer: (Int) -> T
): ObjectBuffer<T> = ): MemoryBuffer<T> =
MutableObjectBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
(0 until size).forEach { (0 until size).forEach {
buffer[it] = initializer(it) buffer[it] = initializer(it)
} }
@ -33,16 +33,28 @@ open class ObjectBuffer<T : Any>(protected val memory: Memory, protected val spe
} }
} }
class MutableObjectBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : ObjectBuffer<T>(memory, spec), class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : MemoryBuffer<T>(memory, spec),
MutableBuffer<T> { MutableBuffer<T> {
private val writer = memory.writer() private val writer = memory.writer()
override fun set(index: Int, value: T) = writer.write(spec, spec.objectSize * index, value) override fun set(index: Int, value: T) = writer.write(spec, spec.objectSize * index, value)
override fun copy(): MutableBuffer<T> = MutableObjectBuffer(memory.copy(), spec) override fun copy(): MutableBuffer<T> = MutableMemoryBuffer(memory.copy(), spec)
companion object { companion object {
fun <T : Any> create(spec: MemorySpec<T>, size: Int) =
MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec)
inline fun <T : Any> create(
spec: MemorySpec<T>,
size: Int,
crossinline initializer: (Int) -> T
) =
MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
(0 until size).forEach {
buffer[it] = initializer(it)
}
}
} }
} }

View File

@ -1,5 +1,7 @@
package scientifik.memory package scientifik.memory
import kotlin.reflect.KClass
/** /**
* A specification to read or write custom objects with fixed size in bytes * A specification to read or write custom objects with fixed size in bytes
*/ */