Merge branch 'dev' into commandertvis/cm-decompositions

# Conflicts:
#	kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt
This commit is contained in:
Iaroslav Postovalov 2021-01-20 16:51:12 +07:00
commit f3c602115a
No known key found for this signature in database
GPG Key ID: 46E15E4A31B3BCD7
15 changed files with 61 additions and 72 deletions

View File

@ -28,7 +28,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark
fun mstExpression() {
val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
}
invokeAndSum(expr)
@ -37,7 +37,7 @@ internal class ExpressionsInterpretersBenchmark {
@Benchmark
fun asmExpression() {
val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
symbol("x") * 2.0 + 2.0 / symbol("x") - 16.0
}.compile()
invokeAndSum(expr)

View File

@ -2,9 +2,8 @@ package kscience.kmath.benchmarks
import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.toEjml
import kscience.kmath.linear.BufferMatrixContext
import kscience.kmath.linear.RealMatrixContext
import kscience.kmath.linear.real
@ -26,11 +25,11 @@ class DotBenchmark {
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val cmMatrix1 = matrix1.toCM()
val cmMatrix2 = matrix2.toCM()
val cmMatrix1 = CMMatrixContext { matrix1.toCM() }
val cmMatrix2 = CMMatrixContext { matrix2.toCM() }
val ejmlMatrix1 = matrix1.toEjml()
val ejmlMatrix2 = matrix2.toEjml()
val ejmlMatrix1 = EjmlMatrixContext { matrix1.toEjml() }
val ejmlMatrix2 = EjmlMatrixContext { matrix2.toEjml() }
}
@Benchmark
@ -49,22 +48,23 @@ class DotBenchmark {
@Benchmark
fun ejmlMultiplicationwithConversion() {
EjmlMatrixContext {
val ejmlMatrix1 = matrix1.toEjml()
val ejmlMatrix2 = matrix2.toEjml()
EjmlMatrixContext {
ejmlMatrix1 dot ejmlMatrix2
}
}
@Benchmark
fun bufferedMultiplication() {
BufferMatrixContext(RealField, Buffer.Companion::real).invoke{
BufferMatrixContext(RealField, Buffer.Companion::real).invoke {
matrix1 dot matrix2
}
}
@Benchmark
fun realMultiplication(){
fun realMultiplication() {
RealMatrixContext {
matrix1 dot matrix2
}

View File

@ -1,25 +0,0 @@
package kscience.kmath.benchmarks
import kscience.kmath.structures.NDField
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State
import org.openjdk.jmh.infra.Blackhole
import kotlin.random.Random
@State(Scope.Benchmark)
class LargeNDBenchmark {
val arraySize = 10000
val RANDOM = Random(222)
val src1 = DoubleArray(arraySize) { RANDOM.nextDouble() }
val src2 = DoubleArray(arraySize) { RANDOM.nextDouble() }
val field = NDField.real(arraySize)
val kmathArray1 = field.produce { (a) -> src1[a] }
val kmathArray2 = field.produce { (a) -> src2[a] }
@Benchmark
fun test10000(bh: Blackhole) {
bh.consume(field.add(kmathArray1, kmathArray2))
}
}

View File

@ -5,10 +5,8 @@ import kotlinx.benchmark.Benchmark
import kscience.kmath.commons.linear.CMMatrixContext
import kscience.kmath.commons.linear.CMMatrixContext.dot
import kscience.kmath.commons.linear.inverse
import kscience.kmath.commons.linear.toCM
import kscience.kmath.ejml.EjmlMatrixContext
import kscience.kmath.ejml.inverse
import kscience.kmath.ejml.toEjml
import kscience.kmath.operations.invoke
import kscience.kmath.structures.Matrix
import org.openjdk.jmh.annotations.Scope
@ -35,16 +33,14 @@ class LinearAlgebraBenchmark {
@Benchmark
fun cmLUPInversion() {
CMMatrixContext {
val cm = matrix.toCM() //avoid overhead on conversion
inverse(cm)
inverse(matrix)
}
}
@Benchmark
fun ejmlInverse() {
EjmlMatrixContext {
val km = matrix.toEjml() //avoid overhead on conversion
inverse(km)
inverse(matrix)
}
}
}

View File

@ -95,8 +95,9 @@ public open class FunctionalExpressionRing<T, A : Ring<T>>(
super<FunctionalExpressionSpace>.binaryOperationFunction(operation)
}
public open class FunctionalExpressionField<T, A : Field<T>>(algebra: A) :
FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> {
public open class FunctionalExpressionField<T, A : Field<T>>(
algebra: A,
) : FunctionalExpressionRing<T, A>(algebra), Field<Expression<T>> {
/**
* Builds an Expression of division an expression by another one.
*/

View File

@ -43,7 +43,7 @@ public class BufferMatrix<T : Any>(
if (this === other) return true
return when (other) {
is NDStructure<*> -> NDStructure.equals(this, other)
is NDStructure<*> -> NDStructure.contentEquals(this, other)
else -> false
}
}

View File

@ -224,6 +224,7 @@ public inline fun <reified T : Comparable<T>, F : Field<T>> GenericMatrixContext
): Matrix<T> = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular)
@OptIn(UnstableKMathAPI::class)
public fun RealMatrixContext.solveWithLUP(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
// Use existing decomposition if it is provided by matrix
val bufferFactory: MutableBufferFactory<Double> = MutableBuffer.Companion::real

View File

@ -3,6 +3,7 @@ package kscience.kmath.operations
import kscience.kmath.memory.MemoryReader
import kscience.kmath.memory.MemorySpec
import kscience.kmath.memory.MemoryWriter
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Buffer
import kscience.kmath.structures.MemoryBuffer
import kscience.kmath.structures.MutableBuffer
@ -41,6 +42,7 @@ private val PI_DIV_2 = Complex(PI / 2, 0)
/**
* A field of [Complex].
*/
@OptIn(UnstableKMathAPI::class)
public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex>, RingWithNumbers<Complex> {
override val zero: Complex = 0.0.toComplex()
override val one: Complex = 1.0.toComplex()

View File

@ -54,7 +54,7 @@ public interface NDStructure<T> {
/**
* Indicates whether some [NDStructure] is equal to another one.
*/
public fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
public fun contentEquals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
if (st1 === st2) return true
// fast comparison of buffers if possible
@ -275,7 +275,7 @@ public abstract class NDBuffer<T> : NDStructure<T> {
override fun elements(): Sequence<Pair<IntArray, T>> = strides.indices().map { it to this[it] }
override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false)
return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
}
override fun hashCode(): Int {

View File

@ -11,7 +11,7 @@ public typealias RealNDElement = BufferedNDFieldElement<Double, RealField>
public class RealNDField(override val shape: IntArray) :
BufferedNDField<Double, RealField>,
ExtendedNDField<Double, RealField, NDBuffer<Double>>,
RingWithNumbers<NDBuffer<Double>>{
RingWithNumbers<NDBuffer<Double>> {
override val strides: Strides = DefaultStrides(shape)
@ -24,35 +24,31 @@ public class RealNDField(override val shape: IntArray) :
return produce { d }
}
private inline fun buildBuffer(size: Int, crossinline initializer: (Int) -> Double): Buffer<Double> =
RealBuffer(DoubleArray(size) { initializer(it) })
/**
* Inline transform an NDStructure to
*/
override fun map(
@Suppress("OVERRIDE_BY_INLINE")
override inline fun map(
arg: NDBuffer<Double>,
transform: RealField.(Double) -> Double
transform: RealField.(Double) -> Double,
): RealNDElement {
check(arg)
val array = buildBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
val array = RealBuffer(arg.strides.linearSize) { offset -> RealField.transform(arg.buffer[offset]) }
return BufferedNDFieldElement(this, array)
}
override fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
val array = buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
@Suppress("OVERRIDE_BY_INLINE")
override inline fun produce(initializer: RealField.(IntArray) -> Double): RealNDElement {
val array = RealBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }
return BufferedNDFieldElement(this, array)
}
override fun mapIndexed(
@Suppress("OVERRIDE_BY_INLINE")
override inline fun mapIndexed(
arg: NDBuffer<Double>,
transform: RealField.(index: IntArray, Double) -> Double
transform: RealField.(index: IntArray, Double) -> Double,
): RealNDElement {
check(arg)
return BufferedNDFieldElement(
this,
buildBuffer(arg.strides.linearSize) { offset ->
RealBuffer(arg.strides.linearSize) { offset ->
elementContext.transform(
arg.strides.index(offset),
arg.buffer[offset]
@ -60,16 +56,17 @@ public class RealNDField(override val shape: IntArray) :
})
}
override fun combine(
@Suppress("OVERRIDE_BY_INLINE")
override inline fun combine(
a: NDBuffer<Double>,
b: NDBuffer<Double>,
transform: RealField.(Double, Double) -> Double
transform: RealField.(Double, Double) -> Double,
): RealNDElement {
check(a, b)
return BufferedNDFieldElement(
this,
buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }
)
val buffer = RealBuffer(strides.linearSize) { offset ->
elementContext.transform(a.buffer[offset], b.buffer[offset])
}
return BufferedNDFieldElement(this, buffer)
}
override fun NDBuffer<Double>.toElement(): FieldElement<NDBuffer<Double>, *, out BufferedNDField<Double, RealField>> =

View File

@ -7,6 +7,7 @@ import kscience.kmath.structures.as2D
import kotlin.test.Test
import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class MatrixTest {
@Test
fun testTranspose() {

View File

@ -8,6 +8,7 @@ import kotlin.math.pow
import kotlin.test.Test
import kotlin.test.assertEquals
@Suppress("UNUSED_VARIABLE")
class NumberNDFieldTest {
val array1: RealNDElement = real2D(3, 3) { i, j -> (i + j).toDouble() }
val array2: RealNDElement = real2D(3, 3) { i, j -> (i - j).toDouble() }

View File

@ -24,7 +24,7 @@ public class LazyNDStructure<T>(
}
public override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false)
return NDStructure.contentEquals(this, other as? NDStructure<*> ?: return false)
}
public override fun hashCode(): Int {

View File

@ -6,6 +6,7 @@ import kscience.kmath.dimensions.DMatrixContext
import kscience.kmath.dimensions.one
import kotlin.test.Test
@Suppress("UNUSED_VARIABLE")
internal class DMatrixContextTest {
@Test
fun testDimensionSafeMatrix() {

View File

@ -3,6 +3,7 @@ package kscience.kmath.ejml
import kscience.kmath.linear.*
import kscience.kmath.misc.UnstableKMathAPI
import kscience.kmath.structures.Matrix
import kscience.kmath.structures.NDStructure
import kscience.kmath.structures.RealBuffer
import org.ejml.dense.row.factory.DecompositionFactory_DDRM
import org.ejml.simple.SimpleMatrix
@ -15,8 +16,11 @@ import kotlin.reflect.cast
* @property origin the underlying [SimpleMatrix].
* @author Iaroslav Postovalov
*/
public inline class EjmlMatrix(public val origin: SimpleMatrix) : Matrix<Double> {
public class EjmlMatrix(
public val origin: SimpleMatrix,
) : Matrix<Double> {
public override val rowNum: Int get() = origin.numRows()
public override val colNum: Int get() = origin.numCols()
@UnstableKMathAPI
@ -82,4 +86,14 @@ public inline class EjmlMatrix(public val origin: SimpleMatrix) : Matrix<Double>
}?.let(type::cast)
public override operator fun get(i: Int, j: Int): Double = origin[i, j]
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Matrix<*>) return false
return NDStructure.contentEquals(this, other)
}
override fun hashCode(): Int = origin.hashCode()
}