forked from kscience/kmath
Initial commit. Algebra operations. NDArray
This commit is contained in:
parent
ed6d56b627
commit
b42beb4b68
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,14 +1,9 @@
|
||||
.gradle
|
||||
/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
.idea/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
19
.idea/gradle.xml
Normal file
19
.idea/gradle.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$USER_HOME$/.posh_gvm/gradle/current" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/common" />
|
||||
<option value="$PROJECT_DIR$/jvm" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
7
.idea/misc.xml
Normal file
7
.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_10" project-jdk-name="10" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
23
.idea/modules/common/common_main.iml
Normal file
23
.idea/modules/common/common_main.iml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="Common (experimental) " useProjectSettings="false">
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="destination" value="$MODULE_DIR$/../../../common/build/classes/kotlin/main" />
|
||||
<option name="classpath" value="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.40/37abadf1cca4450f39672a80d24a379d2fd06356/kotlin-stdlib-common-1.2.40.jar" />
|
||||
<option name="languageVersion" value="1.2" />
|
||||
<option name="apiVersion" value="1.2" />
|
||||
<option name="pluginOptions">
|
||||
<array />
|
||||
</option>
|
||||
<option name="pluginClasspaths">
|
||||
<array />
|
||||
</option>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
23
.idea/modules/common/common_test.iml
Normal file
23
.idea/modules/common/common_test.iml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="Common (experimental) " useProjectSettings="false">
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="destination" value="$MODULE_DIR$/../../../common/build/classes/kotlin/test" />
|
||||
<option name="classpath" value="$MODULE_DIR$/../../../common/build/classes/java/main;D:/Work/Projects/kmath/common/build/classes/kotlin/main;D:/Work/Projects/kmath/common/build/resources/main;C:/Users/darksnake/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-annotations-common/1.2.40/2d4a60c84c93f625f5bffe4cc76b21b243688f5a/kotlin-test-annotations-common-1.2.40.jar;C:/Users/darksnake/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-test-common/1.2.40/2364922e3ca01d51cad8f585a2c8c8d731bb375a/kotlin-test-common-1.2.40.jar;C:/Users/darksnake/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.40/37abadf1cca4450f39672a80d24a379d2fd06356/kotlin-stdlib-common-1.2.40.jar;D:/Work/Projects/kmath/common/build/classes/kotlin/main" />
|
||||
<option name="languageVersion" value="1.2" />
|
||||
<option name="apiVersion" value="1.2" />
|
||||
<option name="pluginOptions">
|
||||
<array />
|
||||
</option>
|
||||
<option name="pluginClasspaths">
|
||||
<array />
|
||||
</option>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
3
build.gradle
Normal file
3
build.gradle
Normal file
@ -0,0 +1,3 @@
|
||||
group = 'scientifik'
|
||||
version = '0.1-SNAPSHOT'
|
||||
|
25
common/build.gradle
Normal file
25
common/build.gradle
Normal file
@ -0,0 +1,25 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.40'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
description = "Platform-independent interfaces for kotlin maths"
|
||||
|
||||
apply plugin: 'kotlin-platform-common'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test-common:$kotlin_version"
|
||||
}
|
||||
|
103
common/src/main/kotlin/scientifik/kmath/operations/Algebra.kt
Normal file
103
common/src/main/kotlin/scientifik/kmath/operations/Algebra.kt
Normal file
@ -0,0 +1,103 @@
|
||||
package scientifik.kmath.operations
|
||||
|
||||
/**
|
||||
* A general interface representing linear context of some kind.
|
||||
* The context defines sum operation for its elements and multiplication by real value.
|
||||
* One must note that in some cases context is a singleton class, but in some cases it
|
||||
* works as a context for operations inside it.
|
||||
*
|
||||
* TODO do we need commutative context?
|
||||
*/
|
||||
interface Space<T> {
|
||||
/**
|
||||
* Neutral element for sum operation
|
||||
*/
|
||||
val zero: T
|
||||
|
||||
/**
|
||||
* Addition operation for two context elements
|
||||
*/
|
||||
fun add(a: T, b: T): T
|
||||
|
||||
/**
|
||||
* Multiplication operation for context element and real number
|
||||
*/
|
||||
fun multiply(a: T, k: Double): T
|
||||
|
||||
//Operation to be performed in this context
|
||||
operator fun T.unaryMinus(): T = multiply(this, -1.0)
|
||||
|
||||
operator fun T.plus(b: T): T = add(this, b)
|
||||
operator fun T.minus(b: T): T = add(this, -b)
|
||||
operator fun T.times(k: Number) = multiply(this, k.toDouble())
|
||||
operator fun T.div(k: Number) = multiply(this, 1.0 / k.toDouble())
|
||||
operator fun Number.times(b: T) = b * this
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The element of linear context
|
||||
* @param S self type of the element. Needed for static type checking
|
||||
*/
|
||||
interface SpaceElement<S : SpaceElement<S>> {
|
||||
/**
|
||||
* The context this element belongs to
|
||||
*/
|
||||
val context: Space<S>
|
||||
|
||||
/**
|
||||
* Self value. Needed for static type checking. Needed to avoid type erasure on JVM.
|
||||
*/
|
||||
val self: S
|
||||
|
||||
operator fun plus(b: S): S = with(context) { self + b }
|
||||
operator fun minus(b: S): S = with(context) { self - b }
|
||||
operator fun times(k: Number): S = with(context) { self * k }
|
||||
operator fun div(k: Number): S = with(context) { self / k }
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as {@link Space} but with additional multiplication operation
|
||||
*/
|
||||
interface Ring<T> : Space<T> {
|
||||
/**
|
||||
* neutral operation for multiplication
|
||||
*/
|
||||
val one: T
|
||||
|
||||
/**
|
||||
* Multiplication for two field elements
|
||||
*/
|
||||
fun multiply(a: T, b: T): T
|
||||
|
||||
operator fun T.times(b: T): T = multiply(this, b)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Ring element
|
||||
*/
|
||||
interface RingElement<S : RingElement<S>> : SpaceElement<S> {
|
||||
override val context: Ring<S>
|
||||
|
||||
operator fun times(b: S): S = with(context) { self * b }
|
||||
}
|
||||
|
||||
/**
|
||||
* Four operations algebra
|
||||
*/
|
||||
interface Field<T> : Ring<T> {
|
||||
fun divide(a: T, b: T): T
|
||||
|
||||
operator fun T.div(b: T): T = divide(this, b)
|
||||
operator fun Double.div(b: T) = this * divide(one, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Field element
|
||||
*/
|
||||
interface FieldElement<S : FieldElement<S>> : RingElement<S> {
|
||||
override val context: Field<S>
|
||||
|
||||
operator fun div(b: S): S = with(context) { self / b }
|
||||
}
|
78
common/src/main/kotlin/scientifik/kmath/operations/Fields.kt
Normal file
78
common/src/main/kotlin/scientifik/kmath/operations/Fields.kt
Normal file
@ -0,0 +1,78 @@
|
||||
package scientifik.kmath.operations
|
||||
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* Field for real values
|
||||
*/
|
||||
object RealField : Field<Real> {
|
||||
override val zero: Real = Real(0.0)
|
||||
override fun add(a: Real, b: Real): Real = Real(a.value + b.value)
|
||||
override val one: Real = Real(1.0)
|
||||
override fun multiply(a: Real, b: Real): Real = Real(a.value * b.value)
|
||||
override fun multiply(a: Real, k: Double): Real = Real(a.value * k)
|
||||
override fun divide(a: Real, b: Real): Real = Real(a.value / b.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Real field element wrapping double
|
||||
*/
|
||||
class Real(val value: Double) : FieldElement<Real>, Number() {
|
||||
override fun toByte(): Byte = value.toByte()
|
||||
override fun toChar(): Char = value.toChar()
|
||||
override fun toDouble(): Double = value
|
||||
override fun toFloat(): Float = value.toFloat()
|
||||
override fun toInt(): Int = value.toInt()
|
||||
override fun toLong(): Long = value.toLong()
|
||||
override fun toShort(): Short = value.toShort()
|
||||
|
||||
//values are dynamically calculated to save memory
|
||||
override val self
|
||||
get() = this
|
||||
override val context
|
||||
get() = RealField
|
||||
}
|
||||
|
||||
/**
|
||||
* A field for complex numbers
|
||||
*/
|
||||
object ComplexField : Field<Complex> {
|
||||
override val zero: Complex = Complex(0.0, 0.0)
|
||||
|
||||
override fun add(a: Complex, b: Complex): Complex = Complex(a.re + b.re, a.im + b.im)
|
||||
|
||||
override fun multiply(a: Complex, k: Double): Complex = Complex(a.re * k, a.im * k)
|
||||
|
||||
override val one: Complex = Complex(1.0, 0.0)
|
||||
|
||||
override fun multiply(a: Complex, b: Complex): Complex = Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re)
|
||||
|
||||
override fun divide(a: Complex, b: Complex): Complex = Complex(a.re * b.re + a.im * b.im, a.re * b.im - a.im * b.re) / b.square
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Complex number class
|
||||
*/
|
||||
data class Complex(val re: Double, val im: Double) : FieldElement<Complex> {
|
||||
override val self: Complex
|
||||
get() = this
|
||||
override val context: Field<Complex>
|
||||
get() = ComplexField
|
||||
|
||||
/**
|
||||
* A complex conjugate
|
||||
*/
|
||||
val conjugate: Complex
|
||||
get() = Complex(re, -im)
|
||||
|
||||
val square: Double
|
||||
get() = re * re + im * im
|
||||
|
||||
val module: Double
|
||||
get() = sqrt(square)
|
||||
|
||||
|
||||
//TODO is it convenient?
|
||||
operator fun not() = conjugate
|
||||
}
|
120
common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt
Normal file
120
common/src/main/kotlin/scientifik/kmath/structures/NDArray.kt
Normal file
@ -0,0 +1,120 @@
|
||||
package scientifik.kmath.structures
|
||||
|
||||
import scientifik.kmath.operations.Field
|
||||
import scientifik.kmath.operations.FieldElement
|
||||
import scientifik.kmath.operations.Real
|
||||
|
||||
class ShapeMismatchException(val expected: List<Int>, val actual: List<Int>) : RuntimeException()
|
||||
|
||||
/**
|
||||
* Field for n-dimensional arrays.
|
||||
* @param shape - the list of dimensions of the array
|
||||
* @param elementField - operations field defined on individual array element
|
||||
*/
|
||||
abstract class NDField<T : FieldElement<T>>(val shape: List<Int>, val elementField: Field<T>) : Field<NDArray<T>> {
|
||||
/**
|
||||
* Create new instance of NDArray using field shape and given initializer
|
||||
*/
|
||||
abstract fun produce(initializer: (List<Int>) -> T): NDArray<T>
|
||||
|
||||
override val zero: NDArray<T>
|
||||
get() = produce { elementField.zero }
|
||||
|
||||
private fun checkShape(vararg arrays: NDArray<T>) {
|
||||
arrays.forEach {
|
||||
if (shape != it.shape) {
|
||||
throw ShapeMismatchException(shape, it.shape)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Element-by-element addition
|
||||
*/
|
||||
override fun add(a: NDArray<T>, b: NDArray<T>): NDArray<T> {
|
||||
checkShape(a, b)
|
||||
return produce { a[it] + b[it] }
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply all elements by cinstant
|
||||
*/
|
||||
override fun multiply(a: NDArray<T>, k: Double): NDArray<T> {
|
||||
checkShape(a)
|
||||
return produce { a[it] * k }
|
||||
}
|
||||
|
||||
override val one: NDArray<T>
|
||||
get() = produce { elementField.one }
|
||||
|
||||
/**
|
||||
* Element-by-element multiplication
|
||||
*/
|
||||
override fun multiply(a: NDArray<T>, b: NDArray<T>): NDArray<T> {
|
||||
checkShape(a)
|
||||
return produce { a[it] * b[it] }
|
||||
}
|
||||
|
||||
/**
|
||||
* Element-by-element division
|
||||
*/
|
||||
override fun divide(a: NDArray<T>, b: NDArray<T>): NDArray<T> {
|
||||
checkShape(a)
|
||||
return produce { a[it] / b[it] }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface NDArray<T : FieldElement<T>> : FieldElement<NDArray<T>>, Iterable<Pair<List<Int>, T>> {
|
||||
|
||||
/**
|
||||
* The list of dimensions of this NDArray
|
||||
*/
|
||||
val shape: List<Int>
|
||||
get() = (context as NDField<T>).shape
|
||||
|
||||
/**
|
||||
* The number of dimentsions for this array
|
||||
*/
|
||||
val dimension: Int
|
||||
get() = shape.size
|
||||
|
||||
/**
|
||||
* Get the element with given indexes. If number of indexes is different from {@link dimension}, throws exception.
|
||||
*/
|
||||
operator fun get(vararg index: Int): T
|
||||
|
||||
operator fun get(index: List<Int>): T {
|
||||
return get(*index.toIntArray())
|
||||
}
|
||||
|
||||
override operator fun iterator(): Iterator<Pair<List<Int>, T>> {
|
||||
return iterateIndexes(shape).map { Pair(it, this[it]) }.iterator()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new NDArray, using given transformation for each element
|
||||
*/
|
||||
fun transform(action: (List<Int>, T) -> T): NDArray<T> = (context as NDField<T>).produce { action(it, this[it]) }
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Iterate over all indexes in the nd-shape
|
||||
*/
|
||||
fun iterateIndexes(shape: List<Int>): Sequence<List<Int>> {
|
||||
return if (shape.size == 1) {
|
||||
(0 until shape[0]).asSequence().map { listOf(it) }
|
||||
} else {
|
||||
val tailShape = ArrayList(shape).apply { remove(0) }
|
||||
val tailSequence: List<List<Int>> = iterateIndexes(tailShape).toList()
|
||||
(0 until shape[0]).asSequence().map { firstIndex ->
|
||||
//adding first element to each of provided index lists
|
||||
tailSequence.map { listOf(firstIndex) + it }.asSequence()
|
||||
}.flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
expect fun RealNDArray(shape: List<Int>, initializer: (List<Int>) -> Double): NDArray<Real>
|
33
jvm/build.gradle
Normal file
33
jvm/build.gradle
Normal file
@ -0,0 +1,33 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.40'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
apply plugin: 'kotlin-platform-jvm'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
expectedBy project(":common")
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
testCompile "junit:junit:4.12"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
sourceCompatibility = "1.8"
|
@ -0,0 +1,64 @@
|
||||
package scientifik.kmath.structures
|
||||
|
||||
import scientifik.kmath.operations.Field
|
||||
import scientifik.kmath.operations.Real
|
||||
import scientifik.kmath.operations.RealField
|
||||
import java.nio.DoubleBuffer
|
||||
|
||||
private class RealNDField(shape: List<Int>) : NDField<Real>(shape, RealField) {
|
||||
|
||||
/**
|
||||
* Strides for memory access
|
||||
*/
|
||||
private val strides: List<Int> by lazy {
|
||||
ArrayList<Int>(shape.size).apply {
|
||||
var current = 1
|
||||
shape.forEach{
|
||||
current *=it
|
||||
add(current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun offset(index: List<Int>): Int {
|
||||
return index.mapIndexed { i, value ->
|
||||
if (value < 0 || value >= shape[i]) {
|
||||
throw RuntimeException("Index out of shape bounds: ($i,$value)")
|
||||
}
|
||||
value * strides[i]
|
||||
}.sum()
|
||||
}
|
||||
|
||||
val capacity: Int
|
||||
get() = strides[shape.size - 1]
|
||||
|
||||
|
||||
override fun produce(initializer: (List<Int>) -> Real): NDArray<Real> {
|
||||
//TODO use sparse arrays for large capacities
|
||||
val buffer = DoubleBuffer.allocate(capacity)
|
||||
NDArray.iterateIndexes(shape).forEach {
|
||||
buffer.put(offset(it), initializer(it).value)
|
||||
}
|
||||
return RealNDArray(buffer)
|
||||
}
|
||||
|
||||
inner class RealNDArray(val data: DoubleBuffer) : NDArray<Real> {
|
||||
|
||||
override val context: Field<NDArray<Real>>
|
||||
get() = this@RealNDField
|
||||
|
||||
override fun get(vararg index: Int): Real {
|
||||
return Real(data.get(offset(index.asList())))
|
||||
}
|
||||
|
||||
override val self: NDArray<Real>
|
||||
get() = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
actual fun RealNDArray(shape: List<Int>, initializer: (List<Int>) -> Double): NDArray<Real> {
|
||||
//TODO cache fields?
|
||||
return RealNDField(shape).produce { Real(initializer(it)) }
|
||||
}
|
4
settings.gradle
Normal file
4
settings.gradle
Normal file
@ -0,0 +1,4 @@
|
||||
rootProject.name = 'kmath'
|
||||
include 'common'
|
||||
include 'jvm'
|
||||
|
Loading…
Reference in New Issue
Block a user