Merge remote-tracking branch 'origin/dev' into adv-expr

# Conflicts:
#	examples/build.gradle.kts
This commit is contained in:
Commander Tvis 2020-06-25 10:28:32 +07:00
commit 002de8d23a
No known key found for this signature in database
GPG Key ID: 70D5F4DCB0972F1B
17 changed files with 330 additions and 50 deletions

View File

@ -2,7 +2,7 @@ plugins {
id("scientifik.publish") apply false id("scientifik.publish") apply false
} }
val kmathVersion by extra("0.1.4-dev-7") val kmathVersion by extra("0.1.4-dev-8")
val bintrayRepo by extra("scientifik") val bintrayRepo by extra("scientifik")
val githubProject by extra("kmath") val githubProject by extra("kmath")

22
docs/codestyle.md Normal file
View File

@ -0,0 +1,22 @@
# Local coding conventions
Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications.
## Utility class names
File name should coincide with a name of one of the classes contained in the file or start with small letter and describe its contents.
The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that file names should start with capital letter even if file does not contain classes. Yet starting utility classes and aggregators with a small letter seems to be a good way to clearly visually separate those files.
This convention could be changed in future in a non-breaking way.
## Private variable names
Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning.
Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define the same entity. It is allowed only for private variables.
This convention could be changed in future in a non-breaking way.
## Functions and properties one-liners
Use one-liners when they occupy single code window line both for functions and properties with getters like `val b: String get() = "fff"`. The same should be done with multiline expressions when they could be cleanly separated.
There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated.

View File

@ -1,13 +1,16 @@
import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
java java
kotlin("jvm") kotlin("jvm")
kotlin("plugin.allopen") kotlin("plugin.allopen") version "1.3.72"
id("kotlinx.benchmark") id("kotlinx.benchmark") version "0.2.0-dev-8"
} }
allOpen.annotation("org.openjdk.jmh.annotations.State") configure<AllOpenExtension> {
annotation("org.openjdk.jmh.annotations.State")
}
repositories { repositories {
maven("http://dl.bintray.com/kyonifer/maven") maven("http://dl.bintray.com/kyonifer/maven")
@ -21,7 +24,6 @@ sourceSets {
} }
dependencies { dependencies {
implementation(project(":kmath-ast"))
implementation(project(":kmath-core")) implementation(project(":kmath-core"))
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))
@ -31,8 +33,8 @@ dependencies {
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation("com.kyonifer:koma-core-ejml:0.12") implementation("com.kyonifer:koma-core-ejml:0.12")
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6")
implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-7") implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8")
"benchmarksCompile"(sourceSets.main.get().compileClasspath) "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath
} }
// Configure benchmark // Configure benchmark

View File

@ -20,7 +20,7 @@ class ViktorBenchmark {
final val viktorField = ViktorNDField(intArrayOf(dim, dim)) final val viktorField = ViktorNDField(intArrayOf(dim, dim))
@Benchmark @Benchmark
fun `Automatic field addition`() { fun automaticFieldAddition() {
autoField.run { autoField.run {
var res = one var res = one
repeat(n) { res += one } repeat(n) { res += one }
@ -28,7 +28,7 @@ class ViktorBenchmark {
} }
@Benchmark @Benchmark
fun `Viktor field addition`() { fun viktorFieldAddition() {
viktorField.run { viktorField.run {
var res = one var res = one
repeat(n) { res += one } repeat(n) { res += one }
@ -36,14 +36,14 @@ class ViktorBenchmark {
} }
@Benchmark @Benchmark
fun `Raw Viktor`() { fun rawViktor() {
val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim)) val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim))
var res = one var res = one
repeat(n) { res = res + one } repeat(n) { res = res + one }
} }
@Benchmark @Benchmark
fun `Real field log`() { fun realdFieldLog() {
realField.run { realField.run {
val fortyTwo = produce { 42.0 } val fortyTwo = produce { 42.0 }
var res = one var res = one
@ -52,7 +52,7 @@ class ViktorBenchmark {
} }
@Benchmark @Benchmark
fun `Raw Viktor log`() { fun rawViktorLog() {
val fortyTwo = F64Array.full(dim, dim, init = 42.0) val fortyTwo = F64Array.full(dim, dim, init = 42.0)
var res: F64Array var res: F64Array
repeat(n) { repeat(n) {

View File

@ -0,0 +1,38 @@
package scientifik.kmath.commons.random
import scientifik.kmath.prob.RandomGenerator
class CMRandomGeneratorWrapper(val factory: (IntArray) -> RandomGenerator) :
org.apache.commons.math3.random.RandomGenerator {
private var generator = factory(intArrayOf())
override fun nextBoolean(): Boolean = generator.nextBoolean()
override fun nextFloat(): Float = generator.nextDouble().toFloat()
override fun setSeed(seed: Int) {
generator = factory(intArrayOf(seed))
}
override fun setSeed(seed: IntArray) {
generator = factory(seed)
}
override fun setSeed(seed: Long) {
setSeed(seed.toInt())
}
override fun nextBytes(bytes: ByteArray) {
generator.fillBytes(bytes)
}
override fun nextInt(): Int = generator.nextInt()
override fun nextInt(n: Int): Int = generator.nextInt(n)
override fun nextGaussian(): Double = TODO()
override fun nextDouble(): Double = generator.nextDouble()
override fun nextLong(): Long = generator.nextLong()
}

View File

@ -0,0 +1,15 @@
package scientifik.kmath.domains
import scientifik.kmath.linear.Point
/**
* A simple geometric domain
*/
interface Domain<T : Any> {
operator fun contains(point: Point<T>): Boolean
/**
* Number of hyperspace dimensions
*/
val dimension: Int
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package scientifik.kmath.domains
import scientifik.kmath.linear.Point
import scientifik.kmath.structures.DoubleBuffer
import scientifik.kmath.structures.indices
/**
*
* HyperSquareDomain class.
*
* @author Alexander Nozik
*/
class HyperSquareDomain(private val lower: DoubleBuffer, private val upper: DoubleBuffer) : RealDomain {
override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i ->
point[i] in lower[i]..upper[i]
}
override val dimension: Int get() = lower.size
override fun getLowerBound(num: Int, point: Point<Double>): Double? = lower[num]
override fun getLowerBound(num: Int): Double? = lower[num]
override fun getUpperBound(num: Int, point: Point<Double>): Double? = upper[num]
override fun getUpperBound(num: Int): Double? = upper[num]
override fun nearestInDomain(point: Point<Double>): Point<Double> {
val res: DoubleArray = DoubleArray(point.size) { i ->
when {
point[i] < lower[i] -> lower[i]
point[i] > upper[i] -> upper[i]
else -> point[i]
}
}
return DoubleBuffer(*res)
}
override fun volume(): Double {
var res = 1.0
for (i in 0 until dimension) {
if (lower[i].isInfinite() || upper[i].isInfinite()) {
return Double.POSITIVE_INFINITY
}
if (upper[i] > lower[i]) {
res *= upper[i] - lower[i]
}
}
return res
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package scientifik.kmath.domains
import scientifik.kmath.linear.Point
/**
* n-dimensional volume
*
* @author Alexander Nozik
*/
interface RealDomain: Domain<Double> {
fun nearestInDomain(point: Point<Double>): Point<Double>
/**
* The lower edge for the domain going down from point
* @param num
* @param point
* @return
*/
fun getLowerBound(num: Int, point: Point<Double>): Double?
/**
* The upper edge of the domain going up from point
* @param num
* @param point
* @return
*/
fun getUpperBound(num: Int, point: Point<Double>): Double?
/**
* Global lower edge
* @param num
* @return
*/
fun getLowerBound(num: Int): Double?
/**
* Global upper edge
* @param num
* @return
*/
fun getUpperBound(num: Int): Double?
/**
* Hyper volume
* @return
*/
fun volume(): Double
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2015 Alexander Nozik.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package scientifik.kmath.domains
import scientifik.kmath.linear.Point
class UnconstrainedDomain(override val dimension: Int) : RealDomain {
override operator fun contains(point: Point<Double>): Boolean = true
override fun getLowerBound(num: Int, point: Point<Double>): Double? = Double.NEGATIVE_INFINITY
override fun getLowerBound(num: Int): Double? = Double.NEGATIVE_INFINITY
override fun getUpperBound(num: Int, point: Point<Double>): Double? = Double.POSITIVE_INFINITY
override fun getUpperBound(num: Int): Double? = Double.POSITIVE_INFINITY
override fun nearestInDomain(point: Point<Double>): Point<Double> = point
override fun volume(): Double = Double.POSITIVE_INFINITY
}

View File

@ -0,0 +1,48 @@
package scientifik.kmath.domains
import scientifik.kmath.linear.Point
import scientifik.kmath.structures.asBuffer
inline class UnivariateDomain(val range: ClosedFloatingPointRange<Double>) : RealDomain {
operator fun contains(d: Double): Boolean = range.contains(d)
override operator fun contains(point: Point<Double>): Boolean {
require(point.size == 0)
return contains(point[0])
}
override fun nearestInDomain(point: Point<Double>): Point<Double> {
require(point.size == 1)
val value = point[0]
return when{
value in range -> point
value >= range.endInclusive -> doubleArrayOf(range.endInclusive).asBuffer()
else -> doubleArrayOf(range.start).asBuffer()
}
}
override fun getLowerBound(num: Int, point: Point<Double>): Double? {
require(num == 0)
return range.start
}
override fun getUpperBound(num: Int, point: Point<Double>): Double? {
require(num == 0)
return range.endInclusive
}
override fun getLowerBound(num: Int): Double? {
require(num == 0)
return range.start
}
override fun getUpperBound(num: Int): Double? {
require(num == 0)
return range.endInclusive
}
override fun volume(): Double = range.endInclusive - range.start
override val dimension: Int get() = 1
}

View File

@ -12,26 +12,23 @@ import scientifik.kmath.structures.asBuffer
import scientifik.kmath.structures.asIterable import scientifik.kmath.structures.asIterable
import kotlin.math.sqrt import kotlin.math.sqrt
typealias RealPoint = Point<Double>
fun DoubleArray.asVector() = RealVector(this.asBuffer()) fun DoubleArray.asVector() = RealVector(this.asBuffer())
fun List<Double>.asVector() = RealVector(this.asBuffer()) fun List<Double>.asVector() = RealVector(this.asBuffer())
object VectorL2Norm : Norm<Point<out Number>, Double> { object VectorL2Norm : Norm<Point<out Number>, Double> {
override fun norm(arg: Point<out Number>): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() }) override fun norm(arg: Point<out Number>): Double = sqrt(arg.asIterable().sumByDouble { it.toDouble() })
} }
inline class RealVector(private val point: Point<Double>) : inline class RealVector(private val point: Point<Double>) :
SpaceElement<Point<Double>, RealVector, VectorSpace<Double, RealField>>, Point<Double> { SpaceElement<RealPoint, RealVector, VectorSpace<Double, RealField>>, RealPoint {
override val context: VectorSpace<Double, RealField> override val context: VectorSpace<Double, RealField> get() = space(point.size)
get() = space(
point.size
)
override fun unwrap(): Point<Double> = point override fun unwrap(): RealPoint = point
override fun Point<Double>.wrap(): RealVector = override fun RealPoint.wrap(): RealVector = RealVector(this)
RealVector(this)
override val size: Int get() = point.size override val size: Int get() = point.size
@ -48,12 +45,8 @@ inline class RealVector(private val point: Point<Double>) :
operator fun invoke(vararg values: Double): RealVector = values.asVector() operator fun invoke(vararg values: Double): RealVector = values.asVector()
fun space(dim: Int): BufferVectorSpace<Double, RealField> = fun space(dim: Int): BufferVectorSpace<Double, RealField> = spaceCache.getOrPut(dim) {
spaceCache.getOrPut(dim) { BufferVectorSpace(dim, RealField) { size, init -> Buffer.real(size, init) }
BufferVectorSpace( }
dim,
RealField
) { size, init -> Buffer.real(size, init) }
}
} }
} }

View File

@ -27,6 +27,10 @@ typealias RealMatrix = Matrix<Double>
fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix = fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix =
MatrixContext.real.produce(rowNum, colNum, initializer) MatrixContext.real.produce(rowNum, colNum, initializer)
fun Array<DoubleArray>.toMatrix(): RealMatrix{
return MatrixContext.real.produce(size, this[0].size) { row, col -> this[row][col] }
}
fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let { fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let {
MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] }
} }

View File

@ -1,18 +1,10 @@
package scientifik.kmath.histogram package scientifik.kmath.histogram
import scientifik.kmath.domains.Domain
import scientifik.kmath.linear.Point import scientifik.kmath.linear.Point
import scientifik.kmath.structures.ArrayBuffer import scientifik.kmath.structures.ArrayBuffer
import scientifik.kmath.structures.DoubleBuffer import scientifik.kmath.structures.DoubleBuffer
/**
* A simple geometric domain
* TODO move to geometry module
*/
interface Domain<T : Any> {
operator fun contains(vector: Point<out T>): Boolean
val dimension: Int
}
/** /**
* The bin in the histogram. The histogram is by definition always done in the real space * The bin in the histogram. The histogram is by definition always done in the real space
*/ */

View File

@ -1,8 +1,8 @@
package scientifik.kmath.histogram package scientifik.kmath.histogram
import scientifik.kmath.linear.Point import scientifik.kmath.linear.Point
import scientifik.kmath.real.asVector
import scientifik.kmath.operations.SpaceOperations import scientifik.kmath.operations.SpaceOperations
import scientifik.kmath.real.asVector
import scientifik.kmath.structures.* import scientifik.kmath.structures.*
import kotlin.math.floor import kotlin.math.floor
@ -21,7 +21,7 @@ data class BinDef<T : Comparable<T>>(val space: SpaceOperations<Point<T>>, val c
class MultivariateBin<T : Comparable<T>>(val def: BinDef<T>, override val value: Number) : Bin<T> { class MultivariateBin<T : Comparable<T>>(val def: BinDef<T>, override val value: Number) : Bin<T> {
override fun contains(vector: Point<out T>): Boolean = def.contains(vector) override fun contains(point: Point<T>): Boolean = def.contains(point)
override val dimension: Int override val dimension: Int
get() = def.center.size get() = def.center.size

View File

@ -16,7 +16,7 @@ class UnivariateBin(val position: Double, val size: Double, val counter: LongCou
operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2)
override fun contains(vector: Buffer<out Double>): Boolean = contains(vector[0]) override fun contains(point: Buffer<Double>): Boolean = contains(point[0])
internal operator fun inc() = this.also { counter.increment() } internal operator fun inc() = this.also { counter.increment() }

View File

@ -10,6 +10,7 @@ interface MemorySpec<T : Any> {
val objectSize: Int val objectSize: Int
fun MemoryReader.read(offset: Int): T fun MemoryReader.read(offset: Int): T
//TODO consider thread safety
fun MemoryWriter.write(offset: Int, value: T) fun MemoryWriter.write(offset: Int, value: T)
} }

View File

@ -11,18 +11,16 @@ class DataViewMemory(val view: DataView) : Memory {
override fun view(offset: Int, length: Int): Memory { override fun view(offset: Int, length: Int): Memory {
require(offset >= 0) { "offset shouldn't be negative: $offset" } require(offset >= 0) { "offset shouldn't be negative: $offset" }
require(length >= 0) { "length shouldn't be negative: $length" } require(length >= 0) { "length shouldn't be negative: $length" }
if (offset + length > size) {
if (offset + length > size)
throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size") throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size")
}
return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length)) return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length))
} }
override fun copy(): Memory = DataViewMemory(DataView(view.buffer.slice(0)))
override fun copy(): Memory { private val reader: MemoryReader = object : MemoryReader {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private val reader = object : MemoryReader {
override val memory: Memory get() = this@DataViewMemory override val memory: Memory get() = this@DataViewMemory
override fun readDouble(offset: Int): Double = view.getFloat64(offset, false) override fun readDouble(offset: Int): Double = view.getFloat64(offset, false)
@ -45,7 +43,7 @@ class DataViewMemory(val view: DataView) : Memory {
override fun reader(): MemoryReader = reader override fun reader(): MemoryReader = reader
private val writer = object : MemoryWriter { private val writer: MemoryWriter = object : MemoryWriter {
override val memory: Memory get() = this@DataViewMemory override val memory: Memory get() = this@DataViewMemory
override fun writeDouble(offset: Int, value: Double) { override fun writeDouble(offset: Int, value: Double) {
@ -76,7 +74,6 @@ class DataViewMemory(val view: DataView) : Memory {
override fun release() { override fun release() {
//does nothing on JS //does nothing on JS
} }
} }
override fun writer(): MemoryWriter = writer override fun writer(): MemoryWriter = writer
@ -94,4 +91,4 @@ actual fun Memory.Companion.allocate(length: Int): Memory {
actual fun Memory.Companion.wrap(array: ByteArray): Memory { actual fun Memory.Companion.wrap(array: ByteArray): Memory {
@Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array @Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array
return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length)) return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length))
} }