Fix EJML inversion issue

This commit is contained in:
Alexander Nozik 2021-09-21 21:24:27 +03:00
parent 9fcc1b3af2
commit 89eebbecb7
43 changed files with 284 additions and 164 deletions

View File

@ -40,6 +40,7 @@
- `FeatureSet` now accepts only `Feature`. It is possible to override keys and use interfaces.
- Use `Symbol` factory function instead of `StringSymbol`
- New discoverability pattern: `<Type>.algebra.<nd/etc>`
- Adjusted commons-math API for linear solvers to match conventions.
### Deprecated
- Specialized `DoubleBufferAlgebra`

View File

@ -105,6 +105,16 @@ benchmark {
commonConfiguration()
include("JafamaBenchmark")
}
configurations.register("viktor") {
commonConfiguration()
include("ViktorBenchmark")
}
configurations.register("viktorLog") {
commonConfiguration()
include("ViktorLogBenchmark")
}
}
// Fix kotlinx-benchmarks bug

View File

@ -13,7 +13,10 @@ import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.structures.Buffer
import kotlin.random.Random
@State(Scope.Benchmark)
@ -35,7 +38,7 @@ internal class DotBenchmark {
@Benchmark
fun cmDot(blackhole: Blackhole) {
CMLinearSpace.run {
CMLinearSpace {
blackhole.consume(cmMatrix1 dot cmMatrix2)
}
}
@ -56,14 +59,14 @@ internal class DotBenchmark {
@Benchmark
fun bufferedDot(blackhole: Blackhole) {
LinearSpace.auto(DoubleField).invoke {
with(DoubleField.linearSpace(Buffer.Companion::auto)) {
blackhole.consume(matrix1 dot matrix2)
}
}
@Benchmark
fun realDot(blackhole: Blackhole) {
LinearSpace.double {
fun doubleDot(blackhole: Blackhole) {
with(Double.algebra.linearSpace) {
blackhole.consume(matrix1 dot matrix2)
}
}

View File

@ -10,13 +10,12 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.commons.linear.inverse
import space.kscience.kmath.commons.linear.lupSolver
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.InverseMatrixFeature
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.linear.lupSolver
import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.algebra
import kotlin.random.Random
@State(Scope.Benchmark)
@ -25,7 +24,7 @@ internal class MatrixInverseBenchmark {
private val random = Random(1224)
private const val dim = 100
private val space = LinearSpace.double
private val space = Double.algebra.linearSpace
//creating invertible matrix
private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
@ -35,20 +34,20 @@ internal class MatrixInverseBenchmark {
@Benchmark
fun kmathLupInversion(blackhole: Blackhole) {
blackhole.consume(LinearSpace.double.lupSolver().inverse(matrix))
blackhole.consume(Double.algebra.linearSpace.lupSolver().inverse(matrix))
}
@Benchmark
fun cmLUPInversion(blackhole: Blackhole) {
CMLinearSpace {
blackhole.consume(inverse(matrix))
blackhole.consume(lupSolver().inverse(matrix))
}
}
@Benchmark
fun ejmlInverse(blackhole: Blackhole) {
EjmlLinearSpaceDDRM {
blackhole.consume(matrix.getFeature<InverseMatrixFeature<Double>>()?.inverse)
blackhole.consume(matrix.toEjml().inverse())
}
}
}

View File

@ -9,7 +9,9 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.autoNdAlgebra
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
@ -46,8 +48,8 @@ internal class NDFieldBenchmark {
private companion object {
private const val dim = 1000
private const val n = 100
private val autoField = DoubleField.autoNd(dim, dim)
private val specializedField = DoubleField.nd(dim, dim)
private val genericField = DoubleField.nd(Buffer.Companion::boxing, dim, dim)
private val autoField = DoubleField.autoNdAlgebra(dim, dim)
private val specializedField = DoubleField.ndAlgebra(dim, dim)
private val genericField = DoubleField.ndAlgebra(Buffer.Companion::boxing, dim, dim)
}
}

View File

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

View File

@ -10,8 +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.autoNd
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.autoNdAlgebra
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.viktor.ViktorFieldND
@ -50,8 +50,8 @@ internal class ViktorLogBenchmark {
private const val n = 100
// automatically build context most suited for given type.
private val autoField = DoubleField.autoNd(dim, dim)
private val realNdField = DoubleField.nd(dim, dim)
private val autoField = DoubleField.autoNdAlgebra(dim, dim)
private val realNdField = DoubleField.ndAlgebra(dim, dim)
private val viktorField = ViktorFieldND(intArrayOf(dim, dim))
}
}

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.withNd
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.nd.withNdAlgebra
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.operations.invoke
fun main(): Unit = DoubleField {
withNd(2, 2) {
fun main(): Unit = Double.algebra {
withNdAlgebra(2, 2) {
//Produce a diagonal StructureND
fun diagonal(v: Double) = produce { (i, j) ->

View File

@ -1,31 +0,0 @@
/*
* 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 file.
*/
package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.complex.nd
import space.kscience.kmath.complex.withNd
import space.kscience.kmath.nd.StructureND
fun main() {
// 2d element
val element = ComplexField.nd(2, 2).produce { (i, j) ->
Complex(i - j, i + j)
}
println(element)
// 1d element operation
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)
(a pow b) + c
}
println(result)
}

View File

@ -0,0 +1,41 @@
/*
* 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 file.
*/
package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.algebra
import space.kscience.kmath.complex.bufferAlgebra
import space.kscience.kmath.complex.ndAlgebra
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.StructureND
fun main() = Complex.algebra {
val complex = 2 + 2 * i
println(complex * 8 - 5 * i)
//flat buffer
val buffer = bufferAlgebra(8).run {
buffer { Complex(it, -it) }.map { Complex(it.im, it.re) }
}
println(buffer)
// 2d element
val element: BufferND<Complex> = ndAlgebra(2, 2).produce { (i, j) ->
Complex(i - j, i + j)
}
println(element)
// 1d element operation
val result: StructureND<Complex> = ndAlgebra(8).run {
val a = produce { (it) -> i * it - it.toDouble() }
val b = 3
val c = Complex(1.0, 1.0)
(a pow b) + c
}
println(result)
}

View File

@ -11,7 +11,7 @@ import space.kscience.kmath.complex.*
import space.kscience.kmath.linear.transpose
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.ndAlgebra
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 = DoubleField.nd(dim, dim)
val complexField: ComplexFieldND = ComplexField.nd(dim, dim)
val realField = DoubleField.ndAlgebra(dim, dim)
val complexField: ComplexFieldND = ComplexField.ndAlgebra(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 {
withNd(4, 8) {
withNdAlgebra(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

@ -9,8 +9,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import org.nd4j.linalg.factory.Nd4j
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.autoNd
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.autoNdAlgebra
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd4j.Nd4jArrayField
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
@ -33,11 +33,11 @@ fun main() {
val n = 1000
// automatically build context most suited for given type.
val autoField = DoubleField.autoNd(dim, dim)
val autoField = DoubleField.autoNdAlgebra(dim, dim)
// specialized nd-field for Double. It works as generic Double field as well.
val realField = DoubleField.nd(dim, dim)
val realField = DoubleField.ndAlgebra(dim, dim)
//A generic boxing field. It should be used for objects, not primitives.
val boxingField = DoubleField.nd(Buffer.Companion::boxing, dim, dim)
val boxingField = DoubleField.ndAlgebra(Buffer.Companion::boxing, dim, dim)
// Nd4j specialized field.
val nd4jField = Nd4jArrayField.real(dim, dim)
//viktor field

View File

@ -6,8 +6,8 @@
package space.kscience.kmath.structures
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.buffer
import space.kscience.kmath.operations.bufferAlgebra
import space.kscience.kmath.operations.produce
inline fun <reified R : Any> MutableBuffer.Companion.same(
n: Int,
@ -17,6 +17,6 @@ inline fun <reified R : Any> MutableBuffer.Companion.same(
fun main() {
with(DoubleField.bufferAlgebra(5)) {
println(number(2.0) + produce(1, 2, 3, 4, 5))
println(number(2.0) + buffer(1, 2, 3, 4, 5))
}
}

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.StructureFeature
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.reflect.KClass
import kotlin.reflect.cast
@ -21,12 +22,15 @@ public class CMMatrix(public val origin: RealMatrix) : Matrix<Double> {
override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j)
}
public class CMVector(public val origin: RealVector) : Point<Double> {
@JvmInline
public value class CMVector(public val origin: RealVector) : Point<Double> {
override val size: Int get() = origin.dimension
override operator fun get(index: Int): Double = origin.getEntry(index)
override operator fun iterator(): Iterator<Double> = origin.toArray().iterator()
override fun toString(): String = Buffer.toString(this)
}
public fun RealVector.toPoint(): CMVector = CMVector(this)

View File

@ -18,7 +18,7 @@ public enum class CMDecomposition {
CHOLESKY
}
public fun CMLinearSpace.solver(
private fun CMLinearSpace.solver(
a: Matrix<Double>,
decomposition: CMDecomposition = CMDecomposition.LUP,
): DecompositionSolver = when (decomposition) {
@ -48,9 +48,11 @@ public fun CMLinearSpace.inverse(
public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver<Double> = object : LinearSolver<Double> {
override fun solve(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> = solve(a, b, decomposition)
override fun solve(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> = solver(a, decomposition).solve(b.toCM().origin).wrap()
override fun solve(a: Matrix<Double>, b: Point<Double>): Point<Double> = solve(a, b, decomposition)
override fun solve(a: Matrix<Double>, b: Point<Double>): Point<Double> = solver(a, decomposition).solve(b.toCM().origin).toPoint()
override fun inverse(matrix: Matrix<Double>): Matrix<Double> = inverse(matrix, decomposition)
}
override fun inverse(matrix: Matrix<Double>): Matrix<Double> = solver(matrix, decomposition).inverse.wrap()
}
public fun CMLinearSpace.lupSolver(): LinearSolver<Double> = solver((CMDecomposition.LUP))

View File

@ -9,8 +9,10 @@ import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.BufferedFieldND
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.BufferField
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.NumbersAddOperations
import space.kscience.kmath.operations.bufferAlgebra
import space.kscience.kmath.structures.Buffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@ -111,13 +113,16 @@ public inline fun BufferedFieldND<Complex, ComplexField>.produceInline(initializ
return BufferND(strides, buffer)
}
@UnstableKMathAPI
public fun ComplexField.bufferAlgebra(size: Int): BufferField<Complex, ComplexField> =
bufferAlgebra(Buffer.Companion::complex, size)
public fun ComplexField.nd(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
public fun ComplexField.ndAlgebra(vararg shape: Int): ComplexFieldND = ComplexFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> ComplexField.withNd(vararg shape: Int, action: ComplexFieldND.() -> R): R {
public inline fun <R> ComplexField.withNdAlgebra(vararg shape: Int, action: ComplexFieldND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ComplexFieldND(shape).action()
}

View File

@ -8,17 +8,15 @@ package space.kscience.kmath.linear
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.BufferedRingND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.nd
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.unwrap
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices
import space.kscience.kmath.structures.*
public class BufferedLinearSpace<T : Any, out A : Ring<T>>(
public class BufferedLinearSpace<T, out A : Ring<T>>(
override val elementAlgebra: A,
private val bufferFactory: BufferFactory<T>,
) : LinearSpace<T, A> {
@ -26,7 +24,7 @@ public class BufferedLinearSpace<T : Any, out A : Ring<T>>(
private fun ndRing(
rows: Int,
cols: Int,
): BufferedRingND<T, A> = elementAlgebra.nd(bufferFactory, rows, cols)
): BufferedRingND<T, A> = elementAlgebra.ndAlgebra(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()
@ -92,3 +90,10 @@ public class BufferedLinearSpace<T : Any, out A : Ring<T>>(
unwrap().map { it * value }.as2D()
}
}
public fun <T, A : Ring<T>> A.linearSpace(bufferFactory: BufferFactory<T>): BufferedLinearSpace<T, A> =
BufferedLinearSpace(this, bufferFactory)
public val DoubleField.linearSpace: BufferedLinearSpace<Double, DoubleField>
get() = BufferedLinearSpace(this, ::DoubleBuffer)

View File

@ -6,8 +6,13 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.StructureFeature
import space.kscience.kmath.nd.as1D
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
@ -34,7 +39,7 @@ public typealias Point<T> = Buffer<T>
* @param T the type of items in the matrices.
* @param A the type of ring over [T].
*/
public interface LinearSpace<T : Any, out A : Ring<T>> {
public interface LinearSpace<T, out A : Ring<T>> {
public val elementAlgebra: A
/**
@ -172,7 +177,8 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
* @return a feature object or `null` if it isn't present.
*/
@UnstableKMathAPI
public fun <F : StructureFeature> computeFeature(structure: Matrix<T>, type: KClass<out F>): F? = structure.getFeature(type)
public fun <F : StructureFeature> computeFeature(structure: Matrix<T>, type: KClass<out F>): F? =
structure.getFeature(type)
public companion object {
@ -184,6 +190,7 @@ public interface LinearSpace<T : Any, out A : Ring<T>> {
bufferFactory: BufferFactory<T> = Buffer.Companion::boxing,
): LinearSpace<T, A> = BufferedLinearSpace(algebra, bufferFactory)
@Deprecated("use DoubleField.linearSpace")
public val double: LinearSpace<Double, DoubleField> = buffered(DoubleField, ::DoubleBuffer)
/**
@ -213,10 +220,8 @@ public inline operator fun <LS : LinearSpace<*, *>, R> LS.invoke(block: LS.() ->
* Convert matrix to vector if it is possible.
*/
public fun <T : Any> Matrix<T>.asVector(): Point<T> =
if (this.colNum == 1)
as1D()
else
error("Can't convert matrix with more than one column to vector")
if (this.colNum == 1) as1D()
else error("Can't convert matrix with more than one column to vector")
/**
* Creates an n &times; 1 [VirtualMatrix], where n is the size of the given buffer.

View File

@ -5,9 +5,7 @@
package space.kscience.kmath.linear
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.BufferAccessor2D
import space.kscience.kmath.structures.DoubleBuffer
@ -214,7 +212,6 @@ internal fun <T : Any> LupDecomposition<T>.solve(
/**
* Produce a generic solver based on LUP decomposition
*/
@PerformancePitfall()
@OptIn(UnstableKMathAPI::class)
public fun <T : Comparable<T>, F : Field<T>> LinearSpace<T, F>.lupSolver(
bufferFactory: MutableBufferFactory<T>,
@ -222,13 +219,12 @@ public fun <T : Comparable<T>, F : Field<T>> LinearSpace<T, F>.lupSolver(
): LinearSolver<T> = object : LinearSolver<T> {
override fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T> {
// Use existing decomposition if it is provided by matrix
val decomposition = a.getFeature() ?: lup(bufferFactory, a, singularityCheck)
val decomposition = computeFeature(a) ?: lup(bufferFactory, a, singularityCheck)
return decomposition.solve(bufferFactory, b)
}
override fun inverse(matrix: Matrix<T>): Matrix<T> = solve(matrix, one(matrix.rowNum, matrix.colNum))
}
@PerformancePitfall
public fun LinearSpace<Double, DoubleField>.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver<Double> =
lupSolver(::DoubleBuffer) { it < singularityThreshold }

View File

@ -8,7 +8,6 @@ package space.kscience.kmath.linear
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.StructureFeature
import space.kscience.kmath.nd.getFeature
import space.kscience.kmath.operations.Ring
import kotlin.reflect.KClass
@ -26,7 +25,6 @@ public class MatrixWrapper<out T : Any> internal constructor(
* Get the first feature matching given class. Does not guarantee that matrix has only one feature matching the
* criteria.
*/
@UnstableKMathAPI
@Suppress("UNCHECKED_CAST")
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? =
features.getFeature(type) ?: origin.getFeature(type)
@ -90,8 +88,7 @@ public class TransposedFeature<out T : Any>(public val original: Matrix<T>) : Ma
/**
* Create a virtual transposed matrix without copying anything. `A.transpose().transpose() === A`
*/
@Suppress("UNCHECKED_CAST")
@OptIn(UnstableKMathAPI::class)
public fun <T : Any> Matrix<T>.transpose(): Matrix<T> = getFeature<TransposedFeature<T>>()?.original ?: VirtualMatrix(
colNum,
rowNum,
) { i, j -> get(j, i) }.withFeature(TransposedFeature(this))
public fun <T : Any> Matrix<T>.transpose(): Matrix<T> = getFeature(TransposedFeature::class)?.original as? Matrix<T>
?: VirtualMatrix(colNum, rowNum) { i, j -> get(j, i) }.withFeature(TransposedFeature(this))

View File

@ -87,39 +87,39 @@ public open class BufferedFieldND<T, out R : Field<T>>(
}
// group factories
public fun <T, A : Group<T>> A.nd(
public fun <T, A : Group<T>> A.ndAlgebra(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedGroupND<T, A> = BufferedGroupND(shape, this, bufferFactory)
@JvmName("withNdGroup")
public inline fun <T, A : Group<T>, R> A.withNd(
public inline fun <T, A : Group<T>, R> A.withNdAlgebra(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedGroupND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return nd(bufferFactory, *shape).run(action)
return ndAlgebra(bufferFactory, *shape).run(action)
}
//ring factories
public fun <T, A : Ring<T>> A.nd(
public fun <T, A : Ring<T>> A.ndAlgebra(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedRingND<T, A> = BufferedRingND(shape, this, bufferFactory)
@JvmName("withNdRing")
public inline fun <T, A : Ring<T>, R> A.withNd(
public inline fun <T, A : Ring<T>, R> A.withNdAlgebra(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedRingND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return nd(bufferFactory, *shape).run(action)
return ndAlgebra(bufferFactory, *shape).run(action)
}
//field factories
public fun <T, A : Field<T>> A.nd(
public fun <T, A : Field<T>> A.ndAlgebra(
bufferFactory: BufferFactory<T>,
vararg shape: Int,
): BufferedFieldND<T, A> = BufferedFieldND(shape, this, bufferFactory)
@ -129,7 +129,7 @@ public fun <T, A : Field<T>> A.nd(
*/
@UnstableKMathAPI
@Suppress("UNCHECKED_CAST")
public inline fun <reified T : Any, A : Field<T>> A.autoNd(
public inline fun <reified T : Any, A : Field<T>> A.autoNdAlgebra(
vararg shape: Int,
): FieldND<T, A> = when (this) {
DoubleField -> DoubleFieldND(shape) as FieldND<T, A>
@ -137,11 +137,11 @@ public inline fun <reified T : Any, A : Field<T>> A.autoNd(
}
@JvmName("withNdField")
public inline fun <T, A : Field<T>, R> A.withNd(
public inline fun <T, A : Field<T>, R> A.withNdAlgebra(
noinline bufferFactory: BufferFactory<T>,
vararg shape: Int,
action: BufferedFieldND<T, A>.() -> R,
): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return nd(bufferFactory, *shape).run(action)
return ndAlgebra(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 DoubleField.nd(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(shape)
/**
* Produce a context for n-dimensional operations inside this real field
*/
public inline fun <R> DoubleField.withNd(vararg shape: Int, action: DoubleFieldND.() -> R): R {
public inline fun <R> DoubleField.withNdAlgebra(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.withNd(vararg shape: Int, action: ShortRingND.() -> R): R {
public inline fun <R> ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ShortRingND(shape).run(action)
}

View File

@ -67,12 +67,14 @@ private class MutableStructure1DWrapper<T>(val structure: MutableStructureND<T>)
structure[intArrayOf(index)] = value
}
@PerformancePitfall
@OptIn(PerformancePitfall::class)
override fun copy(): MutableBuffer<T> = structure
.elements()
.map(Pair<IntArray, T>::second)
.toMutableList()
.asMutableBuffer()
override fun toString(): String = Buffer.toString(this)
}
@ -107,6 +109,8 @@ internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : Mutable
}
override fun copy(): MutableBuffer<T> = buffer.copy()
override fun toString(): String = Buffer.toString(this)
}
/**

View File

@ -6,7 +6,6 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableListBuffer
import space.kscience.kmath.structures.VirtualBuffer
@ -108,7 +107,6 @@ private value class Structure2DWrapper<out T>(val structure: StructureND<T>) : S
override operator fun get(i: Int, j: Int): T = structure[i, j]
@UnstableKMathAPI
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = structure.getFeature(type)
@PerformancePitfall

View File

@ -9,12 +9,12 @@ import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.misc.Feature
import space.kscience.kmath.misc.Featured
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import kotlin.jvm.JvmName
import kotlin.math.abs
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass
@ -61,7 +61,6 @@ public interface StructureND<out T> : Featured<StructureFeature> {
* Feature is some additional structure information that allows to access it special properties or hints.
* If the feature is not present, `null` is returned.
*/
@UnstableKMathAPI
override fun <F : StructureFeature> getFeature(type: KClass<out F>): F? = null
public companion object {
@ -80,6 +79,22 @@ public interface StructureND<out T> : Featured<StructureFeature> {
return st1.elements().all { (index, value) -> value == st2[index] }
}
@PerformancePitfall
public fun contentEquals(
st1: StructureND<Double>,
st2: StructureND<Double>,
tolerance: Double = 1e-11
): Boolean {
if (st1 === st2) return true
// fast comparison of buffers if possible
if (st1 is BufferND && st2 is BufferND && st1.strides == st2.strides)
return Buffer.contentEquals(st1.buffer, st2.buffer)
//element by element comparison if it could not be avoided
return st1.elements().all { (index, value) -> abs(value - st2[index]) < tolerance }
}
/**
* Debug output to string
*/
@ -196,8 +211,8 @@ public fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.contentEquals(
*/
public operator fun <T> StructureND<T>.get(vararg index: Int): T = get(index)
@UnstableKMathAPI
public inline fun <reified T : StructureFeature> StructureND<*>.getFeature(): T? = getFeature(T::class)
//@UnstableKMathAPI
//public inline fun <reified T : StructureFeature> StructureND<*>.getFeature(): T? = getFeature(T::class)
/**
* Represents mutable [StructureND].

View File

@ -17,6 +17,12 @@ import space.kscience.kmath.structures.DoubleBuffer
public interface BufferAlgebra<T, A : Algebra<T>> : Algebra<Buffer<T>> {
public val bufferFactory: BufferFactory<T>
public val elementAlgebra: A
public val size: Int
public fun buffer(vararg elements: T): Buffer<T> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return bufferFactory(size) { elements[it] }
}
//TODO move to multi-receiver inline extension
public fun Buffer<T>.map(block: (T) -> T): Buffer<T> = bufferFactory(size) { block(get(it)) }
@ -39,6 +45,11 @@ public interface BufferAlgebra<T, A : Algebra<T>> : Algebra<Buffer<T>> {
}
}
@UnstableKMathAPI
public fun <T> BufferField<T, *>.buffer(initializer: (Int) -> T): Buffer<T> {
return bufferFactory(size, initializer)
}
@UnstableKMathAPI
public fun <T, A : TrigonometricOperations<T>> BufferAlgebra<T, A>.sin(arg: Buffer<T>): Buffer<T> =
arg.map(elementAlgebra::sin)
@ -104,14 +115,9 @@ public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, p
public class BufferField<T, A : Field<T>>(
override val bufferFactory: BufferFactory<T>,
override val elementAlgebra: A,
public val size: Int
override val size: Int
) : BufferAlgebra<T, A>, Field<Buffer<T>> {
public fun produce(vararg elements: T): Buffer<T> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return bufferFactory(size) { elements[it] }
}
override val zero: Buffer<T> = bufferFactory(size) { elementAlgebra.zero }
override val one: Buffer<T> = bufferFactory(size) { elementAlgebra.one }
@ -135,11 +141,15 @@ public class BufferField<T, A : Field<T>>(
//Double buffer specialization
@UnstableKMathAPI
public fun BufferField<Double, *>.produce(vararg elements: Number): Buffer<Double> {
public fun BufferField<Double, *>.buffer(vararg elements: Number): Buffer<Double> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return bufferFactory(size) { elements[it].toDouble() }
}
@UnstableKMathAPI
public fun <T, A : Field<T>> A.bufferAlgebra(bufferFactory: BufferFactory<T>, size: Int): BufferField<T, A> =
BufferField(bufferFactory, this, size)
@UnstableKMathAPI
public fun DoubleField.bufferAlgebra(size: Int): BufferField<Double, DoubleField> =
BufferField(::DoubleBuffer, DoubleField, size)

View File

@ -23,6 +23,8 @@ public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
override operator fun iterator(): Iterator<T> = array.iterator()
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf())
override fun toString(): String = Buffer.toString(this)
}

View File

@ -45,7 +45,12 @@ public interface Buffer<out T> {
*/
public operator fun iterator(): Iterator<T>
override fun toString(): String
public companion object {
public fun toString(buffer: Buffer<*>): String = buffer.asSequence().joinToString(prefix = "[", separator = ", ", postfix = "]")
/**
* Check the element-by-element match of content of two buffers.
*/
@ -126,6 +131,8 @@ public class VirtualBuffer<out T>(override val size: Int, private val generator:
}
override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map(generator).iterator()
override fun toString(): String = Buffer.toString(this)
}
/**

View File

@ -49,6 +49,8 @@ internal class BufferAccessor2D<T>(
override fun copy(): MutableBuffer<T> = factory(colNum) { get(it) }
override operator fun iterator(): Iterator<T> = (0 until colNum).map(::get).iterator()
override fun toString(): String = Buffer.toString(this)
}
/**

View File

@ -53,8 +53,10 @@ public fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, Valu
/**
* A [Double] buffer that supports flags for each value like `NaN` or Missing.
*/
public class FlaggedDoubleBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>,
Buffer<Double?> {
public class FlaggedDoubleBuffer(
public val values: DoubleArray,
public val flags: ByteArray
) : FlaggedBuffer<Double?>, Buffer<Double?> {
init {
require(values.size == flags.size) { "Values and flags must have the same dimensions" }
}
@ -68,6 +70,8 @@ public class FlaggedDoubleBuffer(public val values: DoubleArray, public val flag
override operator fun iterator(): Iterator<Double?> = values.indices.asSequence().map {
if (isValid(it)) values[it] else null
}.iterator()
override fun toString(): String = Buffer.toString(this)
}
public inline fun FlaggedDoubleBuffer.forEachValid(block: (Double) -> Unit) {

View File

@ -21,6 +21,8 @@ public class ListBuffer<T>(public val list: List<T>) : Buffer<T> {
override operator fun get(index: Int): T = list[index]
override operator fun iterator(): Iterator<T> = list.iterator()
override fun toString(): String = Buffer.toString(this)
}

View File

@ -22,6 +22,8 @@ public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected
override operator fun get(index: Int): T = reader.read(spec, spec.objectSize * index)
override operator fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator()
override fun toString(): String = Buffer.toString(this)
public companion object {
public fun <T : Any> create(spec: MemorySpec<T>, size: Int): MemoryBuffer<T> =
MemoryBuffer(Memory.allocate(size * spec.objectSize), spec)

View File

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

View File

@ -10,7 +10,7 @@ import space.kscience.kmath.misc.PerformancePitfall
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.nd.ndAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Norm
import space.kscience.kmath.operations.invoke
@ -21,7 +21,7 @@ import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest {
val algebra = DoubleField.nd(3, 3)
val algebra = DoubleField.ndAlgebra(3, 3)
val array1 = algebra.produce { (i, j) -> (i + j).toDouble() }
val array2 = algebra.produce { (i, j) -> (i - j).toDouble() }
@ -87,7 +87,7 @@ class NumberNDFieldTest {
@Test
fun testInternalContext() {
algebra {
(DoubleField.nd(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
(DoubleField.ndAlgebra(*array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } }
}
}
}

View File

@ -69,6 +69,8 @@ public class RingBuffer<T>(
@Suppress("NOTHING_TO_INLINE")
private inline fun Int.forward(n: Int): Int = (this + n) % (buffer.size)
override fun toString(): String = Buffer.toString(this)
public companion object {
public inline fun <reified T : Any> build(size: Int, empty: T): RingBuffer<T> {
val buffer = MutableBuffer.auto(size) { empty } as MutableBuffer<T?>

View File

@ -5,9 +5,12 @@
package space.kscience.kmath.ejml
import space.kscience.kmath.linear.InverseMatrixFeature
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.operations.Ring
/**
@ -36,4 +39,7 @@ public abstract class EjmlLinearSpace<T : Any, out A : Ring<T>, out M : org.ejml
): EjmlMatrix<T, M>
public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector<T, M>
@UnstableKMathAPI
public fun EjmlMatrix<T, *>.inverse(): Structure2D<Double> = computeFeature(this, InverseMatrixFeature::class)?.inverse as Structure2D<Double>
}

View File

@ -9,12 +9,11 @@ import org.ejml.data.DMatrixRMaj
import org.ejml.dense.row.CommonOps_DDRM
import org.ejml.dense.row.RandomMatrices_DDRM
import org.ejml.dense.row.factory.DecompositionFactory_DDRM
import space.kscience.kmath.linear.DeterminantFeature
import space.kscience.kmath.linear.LupDecompositionFeature
import space.kscience.kmath.linear.computeFeature
import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.algebra
import kotlin.random.Random
import kotlin.random.asJavaRandom
import kotlin.test.*
@ -82,4 +81,24 @@ internal class EjmlMatrixTest {
val m = randomMatrix
assertSame(m, EjmlDoubleMatrix(m).origin)
}
@Test
fun inverse() = EjmlLinearSpaceDDRM {
val random = Random(1224)
val dim = 20
val space = Double.algebra.linearSpace
//creating invertible matrix
val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = space { l dot u }
val inverted = matrix.toEjml().inverse()
val res = matrix dot inverted
println(StructureND.toString(res))
assertTrue { StructureND.contentEquals(one(dim, dim), res, 1e-3) }
}
}

View File

@ -12,6 +12,7 @@ import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asIterable
@ -32,18 +33,18 @@ import kotlin.math.pow
public typealias RealMatrix = Matrix<Double>
public fun realMatrix(rowNum: Int, colNum: Int, initializer: DoubleField.(i: Int, j: Int) -> Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum, initializer)
Double.algebra.linearSpace.buildMatrix(rowNum, colNum, initializer)
@OptIn(UnstableKMathAPI::class)
public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder<Double, DoubleField> =
LinearSpace.double.matrix(rowNum, colNum)
Double.algebra.linearSpace.matrix(rowNum, colNum)
public fun Array<DoubleArray>.toMatrix(): RealMatrix {
return LinearSpace.double.buildMatrix(size, this[0].size) { row, col -> this@toMatrix[row][col] }
return Double.algebra.linearSpace.buildMatrix(size, this[0].size) { row, col -> this@toMatrix[row][col] }
}
public fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let {
LinearSpace.double.buildMatrix(it.size, it[0].size) { row, col -> it[row][col] }
Double.algebra.linearSpace.buildMatrix(it.size, it[0].size) { row, col -> it[row][col] }
}
public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix =
@ -56,37 +57,37 @@ public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix =
*/
public operator fun RealMatrix.times(double: Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col ->
get(row, col) * double
}
public operator fun RealMatrix.plus(double: Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col ->
get(row, col) + double
}
public operator fun RealMatrix.minus(double: Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col ->
get(row, col) - double
}
public operator fun RealMatrix.div(double: Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col ->
get(row, col) / double
}
public operator fun Double.times(matrix: RealMatrix): RealMatrix =
LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
this@times * matrix[row, col]
}
public operator fun Double.plus(matrix: RealMatrix): RealMatrix =
LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
this@plus + matrix[row, col]
}
public operator fun Double.minus(matrix: RealMatrix): RealMatrix =
LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
Double.algebra.linearSpace.buildMatrix(matrix.rowNum, matrix.colNum) { row, col ->
this@minus - matrix[row, col]
}
@ -101,20 +102,20 @@ public operator fun Double.minus(matrix: RealMatrix): RealMatrix =
@UnstableKMathAPI
public operator fun RealMatrix.times(other: RealMatrix): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> this@times[row, col] * other[row, col] }
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col -> this@times[row, col] * other[row, col] }
public operator fun RealMatrix.plus(other: RealMatrix): RealMatrix =
LinearSpace.double.run { this@plus + other }
Double.algebra.linearSpace.run { this@plus + other }
public operator fun RealMatrix.minus(other: RealMatrix): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> this@minus[row, col] - other[row, col] }
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { row, col -> this@minus[row, col] - other[row, col] }
/*
* Operations on columns
*/
public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer<Double>) -> Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum + 1) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum + 1) { row, col ->
if (col < colNum)
get(row, col)
else
@ -122,7 +123,7 @@ public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer<Double>) -
}
public fun RealMatrix.extractColumns(columnRange: IntRange): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, columnRange.count()) { row, col ->
Double.algebra.linearSpace.buildMatrix(rowNum, columnRange.count()) { row, col ->
this@extractColumns[row, columnRange.first + col]
}
@ -155,14 +156,14 @@ public fun RealMatrix.max(): Double? = elements().map { (_, value) -> value }.ma
public fun RealMatrix.average(): Double = elements().map { (_, value) -> value }.average()
public inline fun RealMatrix.map(crossinline transform: (Double) -> Double): RealMatrix =
LinearSpace.double.buildMatrix(rowNum, colNum) { i, j ->
Double.algebra.linearSpace.buildMatrix(rowNum, colNum) { i, j ->
transform(get(i, j))
}
/**
* Inverse a square real matrix using LUP decomposition
*/
public fun RealMatrix.inverseWithLup(): RealMatrix = LinearSpace.double.lupSolver().inverse(this)
public fun RealMatrix.inverseWithLup(): RealMatrix = Double.algebra.linearSpace.lupSolver().inverse(this)
//extended operations

View File

@ -5,13 +5,14 @@
package space.kscience.kmath.real
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.operations.algebra
/**
* Optimized dot product for real matrices
*/
public infix fun Matrix<Double>.dot(other: Matrix<Double>): Matrix<Double> = LinearSpace.double.run {
public infix fun Matrix<Double>.dot(other: Matrix<Double>): Matrix<Double> = Double.algebra.linearSpace.run {
this@dot dot other
}

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.functions.integrate
import space.kscience.kmath.interpolation.PolynomialInterpolator
import space.kscience.kmath.interpolation.SplineInterpolator
import space.kscience.kmath.interpolation.interpolatePolynomials
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Field
@ -23,6 +24,7 @@ import space.kscience.kmath.structures.map
/**
* Compute analytical indefinite integral of this [PiecewisePolynomial], keeping all intervals intact
*/
@OptIn(PerformancePitfall::class)
@UnstableKMathAPI
public fun <T : Comparable<T>> PiecewisePolynomial<T>.integrate(algebra: Field<T>): PiecewisePolynomial<T> =
PiecewisePolynomial(pieces.map { it.first to it.second.integrate(algebra) })

View File

@ -29,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 = DoubleField.nd(*shape)
override val histogramValueSpace: DoubleFieldND = DoubleField.ndAlgebra(*shape)
override val strides: Strides get() = histogramValueSpace.strides
private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] }

View File

@ -6,10 +6,12 @@
package space.kscience.kmath.viktor
import org.jetbrains.bio.viktor.F64FlatArray
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer
@Suppress("NOTHING_TO_INLINE", "OVERRIDE_BY_INLINE")
public class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer<Double> {
@JvmInline
public value class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer<Double> {
override val size: Int
get() = flatArray.size
@ -21,4 +23,6 @@ public class ViktorBuffer(public val flatArray: F64FlatArray) : MutableBuffer<Do
override fun copy(): MutableBuffer<Double> = ViktorBuffer(flatArray.copy().flatten())
override operator fun iterator(): Iterator<Double> = flatArray.data.iterator()
override fun toString(): String = Buffer.toString(this)
}