Refactor ND builders to suit new discoverability pattern

This commit is contained in:
Alexander Nozik 2021-09-19 20:48:36 +03:00
parent 6dc9e8847a
commit 49ec5d1554
23 changed files with 1130 additions and 75 deletions

View File

@ -46,8 +46,8 @@ internal class NDFieldBenchmark {
private companion object {
private const val dim = 1000
private const val n = 100
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val specializedField = AlgebraND.real(dim, dim)
private val genericField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
private val autoField = DoubleField.autoNd(dim, dim)
private val specializedField = DoubleField.nd(dim, dim)
private val genericField = DoubleField.nd(Buffer.Companion::boxing, dim, dim)
}
}

View File

@ -10,10 +10,9 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real
import space.kscience.kmath.nd.autoNd
import space.kscience.kmath.nd.nd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorNDField
@ -59,8 +58,8 @@ internal class ViktorBenchmark {
private const val n = 100
// automatically build context most suited for given type.
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realField = AlgebraND.real(dim, dim)
private val autoField = DoubleField.autoNd(dim, dim)
private val realField = DoubleField.nd(dim, dim)
private val viktorField = ViktorNDField(dim, dim)
}
}

View File

@ -10,9 +10,8 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.auto
import space.kscience.kmath.nd.real
import space.kscience.kmath.nd.autoNd
import space.kscience.kmath.nd.nd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorFieldND
@ -51,8 +50,8 @@ internal class ViktorLogBenchmark {
private const val n = 100
// automatically build context most suited for given type.
private val autoField = AlgebraND.auto(DoubleField, dim, dim)
private val realNdField = AlgebraND.real(dim, dim)
private val autoField = DoubleField.autoNd(dim, dim)
private val realNdField = DoubleField.nd(dim, dim)
private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
}
}

1020
docs/diagrams/core.puml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,12 @@ import space.kscience.kmath.integration.gaussIntegrator
import space.kscience.kmath.integration.integrate
import space.kscience.kmath.integration.value
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.withNd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
fun main(): Unit = DoubleField {
nd(2, 2) {
withNd(2, 2) {
//Produce a diagonal StructureND
fun diagonal(v: Double) = produce { (i, j) ->

View File

@ -6,18 +6,20 @@
package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.complex
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.withNd
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.autoNd
fun main() {
// 2d element
val element = AlgebraND.complex(2, 2).produce { (i, j) ->
Complex(i.toDouble() - j.toDouble(), i.toDouble() + j.toDouble())
val element = ComplexField.autoNd(2, 2).produce { (i, j) ->
Complex(i - j, i + j)
}
println(element)
// 1d element operation
val result = with(AlgebraND.complex(8)) {
val result: StructureND<Complex> = ComplexField.withNd(8) {
val a = produce { (it) -> i * it - it.toDouble() }
val b = 3
val c = Complex(1.0, 1.0)

View File

@ -9,10 +9,10 @@ package space.kscience.kmath.structures
import space.kscience.kmath.complex.*
import space.kscience.kmath.linear.transpose
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.real
import space.kscience.kmath.nd.nd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import kotlin.system.measureTimeMillis
@ -20,8 +20,8 @@ fun main() {
val dim = 1000
val n = 1000
val realField = AlgebraND.real(dim, dim)
val complexField: ComplexFieldND = AlgebraND.complex(dim, dim)
val realField = DoubleField.nd(dim, dim)
val complexField: ComplexFieldND = ComplexField.nd(dim, dim)
val realTime = measureTimeMillis {
realField {
@ -49,7 +49,7 @@ fun main() {
fun complexExample() {
//Create a context for 2-d structure with complex values
ComplexField {
nd(4, 8) {
withNd(4, 8) {
//a constant real-valued structure
val x = one * 2.5
operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im)

View File

@ -8,7 +8,9 @@ package space.kscience.kmath.structures
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import org.nd4j.linalg.factory.Nd4j
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.autoNd
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd4j.Nd4jArrayField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
@ -31,17 +33,17 @@ fun main() {
val n = 1000
// automatically build context most suited for given type.
val autoField = AlgebraND.auto(DoubleField, dim, dim)
val autoField = DoubleField.autoNd(dim, dim)
// specialized nd-field for Double. It works as generic Double field as well.
val realField = AlgebraND.real(dim, dim)
val realField = DoubleField.nd(dim, dim)
//A generic boxing field. It should be used for objects, not primitives.
val boxingField = AlgebraND.field(DoubleField, Buffer.Companion::boxing, dim, dim)
val boxingField = DoubleField.nd(Buffer.Companion::boxing, dim, dim)
// Nd4j specialized field.
val nd4jField = Nd4jArrayField.real(dim, dim)
//viktor field
val viktorField = ViktorNDField(dim, dim)
//parallel processing based on Java Streams
val parallelField = AlgebraND.realWithStream(dim, dim)
val parallelField = DoubleField.ndStreaming(dim, dim)
measureAndPrint("Boxing addition") {
boxingField {

View File

@ -105,4 +105,4 @@ class StreamDoubleFieldND(override val shape: IntArray) : FieldND<Double, Double
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
}
fun AlgebraND.Companion.realWithStream(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)
fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(shape)

View File

@ -7,6 +7,8 @@ kotlin.code.style=official
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.stability.nowarn=true
kotlin.native.enableDependencyPropagation=false
kotlin.jupyter.add.scanner=false
org.gradle.configureondemand=true
org.gradle.jvmargs=-XX:MaxMetaspaceSize=2G
org.gradle.parallel=true

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.complex
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.BufferedFieldND
import space.kscience.kmath.nd.StructureND
@ -113,12 +112,12 @@ public inline fun BufferedFieldND<Complex, ComplexField>.produceInline(initializ
}
public fun AlgebraND.Companion.complex(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
public fun ComplexField.nd(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> ComplexField.nd(vararg shape: Int, action: ComplexFieldND.() -> R): R {
public inline fun <R> ComplexField.withNd(vararg shape: Int, action: ComplexFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ComplexFieldND(shape).action()
}

View File

@ -2,6 +2,7 @@ plugins {
kotlin("multiplatform")
id("ru.mipt.npm.gradle.common")
id("ru.mipt.npm.gradle.native")
// id("com.xcporter.metaview") version "0.0.5"
}
kotlin.sourceSets {
@ -12,6 +13,12 @@ kotlin.sourceSets {
}
}
//generateUml {
// classTree {
//
// }
//}
readme {
description = "Core classes, algebra definitions, basic linear algebra"
maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT

View File

@ -6,7 +6,10 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.BufferedRingND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.unwrap
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
@ -23,7 +26,7 @@ public class BufferedLinearSpace<T : Any, out A : Ring<T>>(
private fun ndRing(
rows: Int,
cols: Int,
): BufferedRingND<T, A> = AlgebraND.ring(elementAlgebra, bufferFactory, rows, cols)
): BufferedRingND<T, A> = elementAlgebra.nd(bufferFactory, rows, cols)
override fun buildMatrix(rows: Int, columns: Int, initializer: A.(i: Int, j: Int) -> T): Matrix<T> =
ndRing(rows, columns).produce { (i, j) -> elementAlgebra.initializer(i, j) }.as2D()

View File

@ -5,11 +5,13 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public val strides: Strides
@ -85,58 +87,61 @@ public open class BufferedFieldND<T, out R : Field<T>>(
}
// group factories
public fun <T, A : Ring<T>> AlgebraND.Companion.group(
space: A,
public fun <T, A : Group<T>> A.nd(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedGroupND<T, A> = BufferedGroupND(shape, space, bufferFactory)
): BufferedGroupND<T, A> = BufferedGroupND(shape, this, bufferFactory)
public inline fun <T, A : Ring<T>, R> A.ndGroup(
@JvmName("withNdGroup")
public inline fun <T, A : Group<T>, R> A.withNd(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedGroupND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return AlgebraND.group(this, bufferFactory, *shape).run(action)
return nd(bufferFactory, *shape).run(action)
}
//ring factories
public fun <T, A : Ring<T>> AlgebraND.Companion.ring(
ring: A,
public fun <T, A : Ring<T>> A.nd(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedRingND<T, A> = BufferedRingND(shape, ring, bufferFactory)
): BufferedRingND<T, A> = BufferedRingND(shape, this, bufferFactory)
public inline fun <T, A : Ring<T>, R> A.ndRing(
@JvmName("withNdRing")
public inline fun <T, A : Ring<T>, R> A.withNd(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedRingND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return AlgebraND.ring(this, bufferFactory, *shape).run(action)
return nd(bufferFactory, *shape).run(action)
}
//field factories
public fun <T, A : Field<T>> AlgebraND.Companion.field(
field: A,
public fun <T, A : Field<T>> A.nd(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedFieldND<T, A> = BufferedFieldND(shape, field, bufferFactory)
): BufferedFieldND<T, A> = BufferedFieldND(shape, this, bufferFactory)
/**
* Create a [FieldND] for this [Field] inferring proper buffer factory from the type
*/
@UnstableKMathAPI
@Suppress("UNCHECKED_CAST")
public inline fun <reified T : Any, A : Field<T>> AlgebraND.Companion.auto(
field: A,
public inline fun <reified T : Any, A : Field<T>> A.autoNd(
vararg shape: Int,
): FieldND<T, A> = when (field) {
): FieldND<T, A> = when (this) {
DoubleField -> DoubleFieldND(shape) as FieldND<T, A>
else -> BufferedFieldND(shape, field, Buffer.Companion::auto)
else -> BufferedFieldND(shape, this, Buffer.Companion::auto)
}
public inline fun <T, A : Field<T>, R> A.ndField(
@JvmName("withNdField")
public inline fun <T, A : Field<T>, R> A.withNd(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedFieldND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return AlgebraND.field(this, bufferFactory, *shape).run(action)
return nd(bufferFactory, *shape).run(action)
}

View File

@ -103,12 +103,12 @@ public class DoubleFieldND(
override fun atanh(arg: StructureND<Double>): BufferND<Double> = arg.map { atanh(it) }
}
public fun AlgebraND.Companion.real(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
public fun DoubleField.nd(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> DoubleField.nd(vararg shape: Int, action: DoubleFieldND.() -> R): R {
public inline fun <R> DoubleField.withNd(vararg shape: Int, action: DoubleFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return DoubleFieldND(shape).run(action)
}

View File

@ -34,7 +34,7 @@ public class ShortRingND(
public inline fun BufferedRingND<Short, ShortRing>.produceInline(crossinline initializer: ShortRing.(Int) -> Short): BufferND<Short> =
BufferND(strides, ShortBuffer(ShortArray(strides.linearSize) { offset -> ShortRing.initializer(offset) }))
public inline fun <R> ShortRing.nd(vararg shape: Int, action: ShortRingND.() -> R): R {
public inline fun <R> ShortRing.withNd(vararg shape: Int, action: ShortRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ShortRingND(shape).run(action)
}

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.BufferedRingND
import space.kscience.kmath.operations.BigInt.Companion.BASE
import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE
@ -533,5 +532,5 @@ public inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigIn
public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> =
boxing(size, initializer)
public fun AlgebraND.Companion.bigInt(vararg shape: Int): BufferedRingND<BigInt, BigIntField> =
public fun BigIntField.nd(vararg shape: Int): BufferedRingND<BigInt, BigIntField> =
BufferedRingND(shape, BigIntField, Buffer.Companion::bigInt)

View File

@ -5,9 +5,9 @@
package space.kscience.kmath.structures
import space.kscience.kmath.nd.AlgebraND
import space.kscience.kmath.nd.get
import space.kscience.kmath.nd.real
import space.kscience.kmath.nd.nd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.testutils.FieldVerifier
import kotlin.test.Test
@ -16,12 +16,12 @@ import kotlin.test.assertEquals
internal class NDFieldTest {
@Test
fun verify() {
(AlgebraND.real(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) }
(DoubleField.nd(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) }
}
@Test
fun testStrides() {
val ndArray = AlgebraND.real(10, 10).produce { (it[0] + it[1]).toDouble() }
val ndArray = DoubleField.nd(10, 10).produce { (it[0] + it[1]).toDouble() }
assertEquals(ndArray[5, 5], 10.0)
}
}

View File

@ -7,7 +7,11 @@ package space.kscience.kmath.structures
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.combine
import space.kscience.kmath.nd.get
import space.kscience.kmath.nd.nd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Norm
import space.kscience.kmath.operations.invoke
import kotlin.math.abs
@ -17,7 +21,7 @@ import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest {
val algebra = AlgebraND.real(3, 3)
val algebra = DoubleField.nd(3, 3)
val array1 = algebra.produce { (i, j) -> (i + j).toDouble() }
val array2 = algebra.produce { (i, j) -> (i - j).toDouble() }
@ -83,7 +87,7 @@ class NumberNDFieldTest {
@Test
fun testInternalContext() {
algebra {
(AlgebraND.real(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
(DoubleField.nd(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
}
}
}

View File

@ -9,6 +9,7 @@ import space.kscience.kmath.domains.Domain
import space.kscience.kmath.domains.HyperSquareDomain
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.*
import kotlin.math.floor
@ -28,7 +29,7 @@ public class DoubleHistogramSpace(
public val dimension: Int get() = lower.size
private val shape = IntArray(binNums.size) { binNums[it] + 2 }
override val histogramValueSpace: DoubleFieldND = AlgebraND.real(*shape)
override val histogramValueSpace: DoubleFieldND = DoubleField.nd(*shape)
override val strides: Strides get() = histogramValueSpace.strides
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }

View File

@ -11,7 +11,6 @@ import kotlinx.html.stream.createHTML
import kotlinx.html.unsafe
import org.jetbrains.kotlinx.jupyter.api.DisplayResult
import org.jetbrains.kotlinx.jupyter.api.HTML
import org.jetbrains.kotlinx.jupyter.api.annotations.JupyterLibrary
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess
import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer

View File

@ -16,6 +16,14 @@ import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.DoubleL2Norm
public class QowRuns(public val runs: Int) : OptimizationFeature {
init {
require(runs >= 1) { "Number of runs must be more than zero" }
}
override fun toString(): String = "QowRuns(runs=$runs)"
}
/**
* An optimizer based onf Fyodor Tkachev's quasi-optimal weights method.
@ -56,7 +64,7 @@ public object QowOptimizer : Optimizer<Double, XYFit> {
val prior: DifferentiableExpression<Double>? get() = problem.getFeature<OptimizationPrior<Double>>()
override fun toString(): String = parameters.toString()
override fun toString(): String = parameters.toString()
}
/**
@ -242,9 +250,15 @@ public object QowOptimizer : Optimizer<Double, XYFit> {
}
override suspend fun optimize(problem: XYFit): XYFit {
val qowSteps = 2
val initialWeight = QoWeight(problem, problem.startPoint)
val res = initialWeight.newtonianRun()
val qowRuns = problem.getFeature<QowRuns>()?.runs ?: 2
var qow = QoWeight(problem, problem.startPoint)
var res = qow.newtonianRun()
repeat(qowRuns - 1) {
qow = QoWeight(problem, res.parameters)
res = qow.newtonianRun()
}
return res.problem.withFeature(OptimizationResult(res.parameters))
}
}

View File

@ -5,11 +5,11 @@ pluginManagement {
gradlePluginPortal()
}
val kotlinVersion = "1.5.21"
val kotlinVersion = "1.5.30"
plugins {
id("org.jetbrains.kotlinx.benchmark") version "0.3.1"
id("ru.mipt.npm.gradle.project") version "0.10.2"
id("ru.mipt.npm.gradle.project") version "0.10.3"
kotlin("multiplatform") version kotlinVersion
kotlin("plugin.allopen") version kotlinVersion
}