Add sparse matrix builder
This commit is contained in:
parent
676cf5ff3b
commit
c70a7c5128
CHANGELOG.md
benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks
build.gradle.ktsexamples/src/main/kotlin/space/kscience/kmath/operations
kmath-core/src
commonMain/kotlin/space/kscience/kmath
linear
nd
operations
structures
commonTest/kotlin/space/kscience/kmath/linear
jvmTest/kotlin/space/kscience/kmath/linear
kmath-for-real/src
commonMain/kotlin/space/kscience/kmath/real
commonTest/kotlin/space/kscience/kmath/real
kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d
kmath-ojalgo
kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Convenient matrix builders for rows, columns, vstacks and hstacks
|
- Convenient matrix builders for rows, columns, vstacks and hstacks
|
||||||
|
- Spreadsheet matrix builder
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import kotlinx.benchmark.Benchmark
|
|||||||
import kotlinx.benchmark.Blackhole
|
import kotlinx.benchmark.Blackhole
|
||||||
import kotlinx.benchmark.Scope
|
import kotlinx.benchmark.Scope
|
||||||
import kotlinx.benchmark.State
|
import kotlinx.benchmark.State
|
||||||
|
import space.kscience.kmath.linear.MatrixBuilder
|
||||||
import space.kscience.kmath.linear.linearSpace
|
import space.kscience.kmath.linear.linearSpace
|
||||||
import space.kscience.kmath.linear.matrix
|
|
||||||
import space.kscience.kmath.linear.symmetric
|
import space.kscience.kmath.linear.symmetric
|
||||||
import space.kscience.kmath.operations.Float64Field
|
import space.kscience.kmath.operations.Float64Field
|
||||||
import space.kscience.kmath.tensors.core.symEigJacobi
|
import space.kscience.kmath.tensors.core.symEigJacobi
|
||||||
@ -24,7 +24,7 @@ internal class TensorAlgebraBenchmark {
|
|||||||
private val random = Random(12224)
|
private val random = Random(12224)
|
||||||
private const val dim = 30
|
private const val dim = 30
|
||||||
|
|
||||||
private val matrix = Float64Field.linearSpace.matrix(dim, dim).symmetric { _, _ -> random.nextDouble() }
|
private val matrix = Float64Field.linearSpace.MatrixBuilder(dim, dim).symmetric { _, _ -> random.nextDouble() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
|
@ -14,7 +14,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.4.1"
|
version = "0.4.2-dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
|
@ -6,22 +6,21 @@
|
|||||||
package space.kscience.kmath.operations
|
package space.kscience.kmath.operations
|
||||||
|
|
||||||
import space.kscience.kmath.commons.linear.CMLinearSpace
|
import space.kscience.kmath.commons.linear.CMLinearSpace
|
||||||
import space.kscience.kmath.linear.matrix
|
import space.kscience.kmath.linear.MatrixBuilder
|
||||||
|
import space.kscience.kmath.linear.fill
|
||||||
import space.kscience.kmath.nd.Float64BufferND
|
import space.kscience.kmath.nd.Float64BufferND
|
||||||
import space.kscience.kmath.nd.Structure2D
|
import space.kscience.kmath.nd.Structure2D
|
||||||
import space.kscience.kmath.nd.mutableStructureND
|
import space.kscience.kmath.nd.mutableStructureND
|
||||||
import space.kscience.kmath.nd.ndAlgebra
|
import space.kscience.kmath.nd.ndAlgebra
|
||||||
import space.kscience.kmath.structures.Float64
|
import space.kscience.kmath.structures.Float64
|
||||||
import space.kscience.kmath.viktor.viktorAlgebra
|
import space.kscience.kmath.viktor.viktorAlgebra
|
||||||
import kotlin.collections.component1
|
|
||||||
import kotlin.collections.component2
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val viktorStructure = Float64Field.viktorAlgebra.mutableStructureND(2, 2) { (i, j) ->
|
val viktorStructure = Float64Field.viktorAlgebra.mutableStructureND(2, 2) { (i, j) ->
|
||||||
if (i == j) 2.0 else 0.0
|
if (i == j) 2.0 else 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
val cmMatrix: Structure2D<Float64> = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0)
|
val cmMatrix: Structure2D<Float64> = CMLinearSpace.MatrixBuilder(2, 2).fill(0.0, 1.0, 0.0, 3.0)
|
||||||
|
|
||||||
val res: Float64BufferND = Float64Field.ndAlgebra {
|
val res: Float64BufferND = Float64Field.ndAlgebra {
|
||||||
exp(viktorStructure) + 2.0 * cmMatrix
|
exp(viktorStructure) + 2.0 * cmMatrix
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.linear
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
import space.kscience.attributes.FlagAttribute
|
import space.kscience.attributes.Attributes
|
||||||
import space.kscience.attributes.SafeType
|
import space.kscience.attributes.SafeType
|
||||||
import space.kscience.attributes.WithType
|
import space.kscience.attributes.WithType
|
||||||
import space.kscience.kmath.UnstableKMathAPI
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
@ -13,62 +13,51 @@ import space.kscience.kmath.operations.Ring
|
|||||||
import space.kscience.kmath.structures.BufferAccessor2D
|
import space.kscience.kmath.structures.BufferAccessor2D
|
||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
import space.kscience.kmath.structures.MutableBufferFactory
|
||||||
|
|
||||||
public class MatrixBuilder<T : Any, out A : Ring<T>>(
|
/**
|
||||||
|
* A builder for matrix with fixed size
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public class MatrixBuilder<T, out A : Ring<T>>(
|
||||||
public val linearSpace: LinearSpace<T, A>,
|
public val linearSpace: LinearSpace<T, A>,
|
||||||
public val rows: Int,
|
public val rowNum: Int,
|
||||||
public val columns: Int,
|
public val colNum: Int,
|
||||||
) : WithType<T> {
|
) : WithType<T> {
|
||||||
|
|
||||||
override val type: SafeType<T> get() = linearSpace.type
|
override val type: SafeType<T> get() = linearSpace.type
|
||||||
|
|
||||||
public operator fun invoke(vararg elements: T): Matrix<T> {
|
}
|
||||||
require(rows * columns == elements.size) { "The number of elements ${elements.size} is not equal $rows * $columns" }
|
|
||||||
return linearSpace.buildMatrix(rows, columns) { i, j -> elements[i * columns + j] }
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO add specific matrix builder functions like diagonal, etc
|
@UnstableKMathAPI
|
||||||
|
public fun <T, A : Ring<T>> MatrixBuilder<T, A>.sparse(): SparseMatrix<T> =
|
||||||
|
SparseMatrix(rowNum, colNum, linearSpace.elementAlgebra.zero)
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T, A : Ring<T>> MatrixBuilder<T, A>.fill(vararg elements: T): Matrix<T> {
|
||||||
|
require(rowNum * colNum == elements.size) { "The number of elements ${elements.size} is not equal $rowNum * $colNum" }
|
||||||
|
return linearSpace.buildMatrix(rowNum, colNum) { i, j -> elements[i * colNum + j] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a matrix builder with given number of rows and columns
|
* Create a matrix builder with given number of rows and columns
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public fun <T : Any, A : Ring<T>> LinearSpace<T, A>.matrix(rows: Int, columns: Int): MatrixBuilder<T, A> =
|
public fun <T, A : Ring<T>> LinearSpace<T, A>.MatrixBuilder(rows: Int, columns: Int): MatrixBuilder<T, A> =
|
||||||
MatrixBuilder(this, rows, columns)
|
MatrixBuilder(this, rows, columns)
|
||||||
|
|
||||||
@UnstableKMathAPI
|
|
||||||
public fun <T : Any> LinearSpace<T, Ring<T>>.vector(vararg elements: T): Point<T> {
|
|
||||||
return buildVector(elements.size) { elements[it] }
|
|
||||||
}
|
|
||||||
|
|
||||||
public inline fun <T : Any> LinearSpace<T, Ring<T>>.row(
|
|
||||||
size: Int,
|
|
||||||
crossinline builder: (Int) -> T,
|
|
||||||
): Matrix<T> = buildMatrix(1, size) { _, j -> builder(j) }
|
|
||||||
|
|
||||||
public fun <T : Any> LinearSpace<T, Ring<T>>.row(vararg values: T): Matrix<T> = row(values.size, values::get)
|
|
||||||
|
|
||||||
public inline fun <T : Any> LinearSpace<T, Ring<T>>.column(
|
|
||||||
size: Int,
|
|
||||||
crossinline builder: (Int) -> T,
|
|
||||||
): Matrix<T> = buildMatrix(size, 1) { i, _ -> builder(i) }
|
|
||||||
|
|
||||||
public fun <T : Any> LinearSpace<T, Ring<T>>.column(vararg values: T): Matrix<T> = column(values.size, values::get)
|
|
||||||
|
|
||||||
public object Symmetric : MatrixAttribute<Unit>, FlagAttribute
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Naive implementation of a symmetric matrix builder, that adds a [Symmetric] tag. The resulting matrix contains
|
* Naive implementation of a symmetric matrix builder, that adds a [Symmetric] tag.
|
||||||
* full `size^2` number of elements, but caches elements during calls to save [builder] calls. [builder] is always called in the
|
* The resulting matrix contains full `size^2` number of elements,
|
||||||
* upper triangle region meaning that `i <= j`
|
* but caches elements during calls to save [builder] calls.
|
||||||
|
* Always called in the upper triangle region meaning that `i <= j`
|
||||||
*/
|
*/
|
||||||
public fun <T : Any, A : Ring<T>> MatrixBuilder<T, A>.symmetric(
|
@UnstableKMathAPI
|
||||||
builder: (i: Int, j: Int) -> T,
|
public fun <T, A : Ring<T>> MatrixBuilder<T, A>.symmetric(
|
||||||
|
builder: A.(i: Int, j: Int) -> T,
|
||||||
): Matrix<T> {
|
): Matrix<T> {
|
||||||
require(columns == rows) { "In order to build symmetric matrix, number of rows $rows should be equal to number of columns $columns" }
|
require(colNum == rowNum) { "In order to build symmetric matrix, number of rows $rowNum should be equal to number of columns $colNum" }
|
||||||
return with(BufferAccessor2D<T?>(rows, rows, MutableBufferFactory(type))) {
|
return with(BufferAccessor2D<T?>(rowNum, rowNum, MutableBufferFactory(type))) {
|
||||||
val cache = HashMap<IntArray, T>()
|
val cache = HashMap<IntArray, T>()
|
||||||
linearSpace.buildMatrix(rows, rows) { i, j ->
|
linearSpace.buildMatrix(this@symmetric.rowNum, this@symmetric.rowNum) { i, j ->
|
||||||
val index = intArrayOf(i, j)
|
val index = intArrayOf(i, j)
|
||||||
val cached = cache[index]
|
val cached = cache[index]
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
@ -81,4 +70,50 @@ public fun <T : Any, A : Ring<T>> MatrixBuilder<T, A>.symmetric(
|
|||||||
}
|
}
|
||||||
}.withAttribute(Symmetric)
|
}.withAttribute(Symmetric)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a diagonal matrix with given factory.
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T, A : Ring<T>> MatrixBuilder<T, A>.diagonal(
|
||||||
|
builder: A.(Int) -> T
|
||||||
|
): Matrix<T> = with(linearSpace.elementAlgebra) {
|
||||||
|
require(colNum == rowNum) { "In order to build symmetric matrix, number of rows $rowNum should be equal to number of columns $colNum" }
|
||||||
|
return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal)) { i, j ->
|
||||||
|
check(i in 0 until rowNum) { "$i out of bounds: 0..<$rowNum" }
|
||||||
|
check(j in 0 until colNum) { "$j out of bounds: 0..<$colNum" }
|
||||||
|
if (i == j) {
|
||||||
|
builder(i)
|
||||||
|
} else {
|
||||||
|
zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a diagonal matrix from elements
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T> MatrixBuilder<T, Ring<T>>.diagonal(vararg elements: T): Matrix<T> {
|
||||||
|
require(colNum == rowNum) { "In order to build symmetric matrix, number of rows $rowNum should be equal to number of columns $colNum" }
|
||||||
|
return return VirtualMatrix(rowNum, colNum, attributes = Attributes(IsDiagonal)) { i, j ->
|
||||||
|
check(i in 0 until rowNum) { "$i out of bounds: 0..<$rowNum" }
|
||||||
|
check(j in 0 until colNum) { "$j out of bounds: 0..<$colNum" }
|
||||||
|
if (i == j) {
|
||||||
|
elements[i]
|
||||||
|
} else {
|
||||||
|
linearSpace.elementAlgebra.zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a lazily evaluated virtual matrix with a given size
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T : Any> MatrixBuilder<T, *>.virtual(
|
||||||
|
attributes: Attributes = Attributes.EMPTY,
|
||||||
|
generator: (i: Int, j: Int) -> T,
|
||||||
|
): VirtualMatrix<T> = VirtualMatrix(rowNum, colNum, attributes, generator)
|
||||||
|
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2025 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
|
import space.kscience.attributes.Attributes
|
||||||
|
import space.kscience.kmath.PerformancePitfall
|
||||||
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.operations.Ring
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutable sparse matrix that stores values only for non-zero cells ([DOK format](https://en.wikipedia.org/wiki/Sparse_matrix#Dictionary_of_keys_(DOK))).
|
||||||
|
*
|
||||||
|
* [SparseMatrix] is ineffective, but does not depend on particular [LinearSpace]
|
||||||
|
*
|
||||||
|
* Using this class is almost always a [PerformancePitfall]. It should be used only for special cases like manual matrix building.
|
||||||
|
*/
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public class SparseMatrix<T>(
|
||||||
|
override val rowNum: Int,
|
||||||
|
override val colNum: Int,
|
||||||
|
private val zero: T,
|
||||||
|
cells: Map<Pair<Int, Int>, T> = emptyMap(),
|
||||||
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
|
) : MutableMatrix<T> {
|
||||||
|
|
||||||
|
private val cells = cells.toMutableMap()
|
||||||
|
|
||||||
|
override fun get(i: Int, j: Int): T {
|
||||||
|
if (i !in 0 until rowNum) throw IndexOutOfBoundsException("Row index $i out of row range 0..$rowNum")
|
||||||
|
if (j !in 0 until colNum) throw IndexOutOfBoundsException("Column index $j out of column range 0..$colNum")
|
||||||
|
return cells[i to j] ?: zero
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(i: Int, j: Int, value: T) {
|
||||||
|
require(i in 0 until rowNum) { "Row index $i is out of bounds: 0..<$rowNum" }
|
||||||
|
require(j in 0 until colNum) { "Row index $j is out of bounds: 0..<$colNum" }
|
||||||
|
val coordinates = i to j
|
||||||
|
if (cells[coordinates] != null || value != zero) {
|
||||||
|
cells[coordinates] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(index: IntArray, value: T) {
|
||||||
|
require(index.size == 2) { "Index array must contain two elements." }
|
||||||
|
set(index[0], index[1], value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T> SparseMatrix<T>.fill(vararg elements: T): SparseMatrix<T> {
|
||||||
|
require(rowNum * colNum == elements.size) { "The number of elements ${elements.size} is not equal $rowNum * $colNum" }
|
||||||
|
for (i in 0 until rowNum) {
|
||||||
|
for (j in 0 until colNum) {
|
||||||
|
set(i, j, elements[i * rowNum + j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.sparse(
|
||||||
|
rows: Int,
|
||||||
|
columns: Int,
|
||||||
|
attributes: Attributes = Attributes.EMPTY,
|
||||||
|
builder: SparseMatrix<T>.() -> Unit = {}
|
||||||
|
): SparseMatrix<T> = SparseMatrix(rows, columns, elementAlgebra.zero, attributes = attributes).apply(builder)
|
@ -6,7 +6,6 @@
|
|||||||
package space.kscience.kmath.linear
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
import space.kscience.attributes.Attributes
|
import space.kscience.attributes.Attributes
|
||||||
import space.kscience.kmath.nd.ShapeND
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,13 +19,5 @@ public class VirtualMatrix<out T>(
|
|||||||
override val attributes: Attributes = Attributes.EMPTY,
|
override val attributes: Attributes = Attributes.EMPTY,
|
||||||
public val generator: (i: Int, j: Int) -> T,
|
public val generator: (i: Int, j: Int) -> T,
|
||||||
) : Matrix<T> {
|
) : Matrix<T> {
|
||||||
|
|
||||||
override val shape: ShapeND get() = ShapeND(rowNum, colNum)
|
|
||||||
|
|
||||||
override operator fun get(i: Int, j: Int): T = generator(i, j)
|
override operator fun get(i: Int, j: Int): T = generator(i, j)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun <T : Any> MatrixBuilder<T, *>.virtual(
|
|
||||||
attributes: Attributes = Attributes.EMPTY,
|
|
||||||
generator: (i: Int, j: Int) -> T,
|
|
||||||
): VirtualMatrix<T> = VirtualMatrix(rows, columns, attributes, generator)
|
|
@ -23,10 +23,17 @@ public interface MatrixScope<T> : WithType<T>
|
|||||||
*/
|
*/
|
||||||
public interface MatrixAttribute<T> : StructureAttribute<T>
|
public interface MatrixAttribute<T> : StructureAttribute<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matrices with this feature are symmetric, meaning `matrix[i,j] == matrix[j,i]`
|
||||||
|
*/
|
||||||
|
public interface Symmetric : MatrixAttribute<Unit>, FlagAttribute{
|
||||||
|
public companion object: Symmetric
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matrices with this feature are considered to have only diagonal non-zero elements.
|
* Matrices with this feature are considered to have only diagonal non-zero elements.
|
||||||
*/
|
*/
|
||||||
public interface IsDiagonal : MatrixAttribute<Unit>, FlagAttribute {
|
public interface IsDiagonal : Symmetric {
|
||||||
public companion object : IsDiagonal
|
public companion object : IsDiagonal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2025 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
|
import space.kscience.kmath.PerformancePitfall
|
||||||
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.operations.Ring
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a vector from elements
|
||||||
|
*/
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.vector(vararg elements: T): Point<T> =
|
||||||
|
buildVector(elements.size) { elements[it] }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single row matrix
|
||||||
|
*/
|
||||||
|
public inline fun <T, A : Ring<T>> LinearSpace<T, A>.row(
|
||||||
|
size: Int,
|
||||||
|
crossinline builder: A.(Int) -> T,
|
||||||
|
): Matrix<T> = buildMatrix(1, size) { _, j -> builder(j) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single row matrix from elements
|
||||||
|
*/
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.row(vararg elements: T): Matrix<T> = row(elements.size) { elements[it] }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single column matrix
|
||||||
|
*/
|
||||||
|
public inline fun <T, A : Ring<T>> LinearSpace<T, A>.column(
|
||||||
|
size: Int,
|
||||||
|
crossinline builder: A.(Int) -> T,
|
||||||
|
): Matrix<T> = buildMatrix(size, 1) { i, _ -> builder(i) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a single column matrix from elements
|
||||||
|
*/
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.column(vararg elements: T): Matrix<T> = column(elements.size) { elements[it] }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack vertically several matrices with the same number of columns.
|
||||||
|
*
|
||||||
|
* Resulting matrix number of rows is the sum of rows in all [matrices]
|
||||||
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.vstack(vararg matrices: Matrix<T>): Matrix<T> {
|
||||||
|
require(matrices.isNotEmpty()) { "No matrices" }
|
||||||
|
val colNum = matrices.first().colNum
|
||||||
|
require(matrices.all { it.colNum == colNum }) { "All matrices must have the same number of columns: $colNum" }
|
||||||
|
|
||||||
|
val rows = matrices.flatMap { it.rows }
|
||||||
|
|
||||||
|
return buildMatrix(matrices.sumOf { it.rowNum }, colNum) { row, column->
|
||||||
|
rows[row][column]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack horizontally several matrices with the same number of rows.
|
||||||
|
*
|
||||||
|
* Resulting matrix number of co is the sum of rows in all [matrices]
|
||||||
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <T> LinearSpace<T, Ring<T>>.hstack(vararg matrices: Matrix<T>): Matrix<T> {
|
||||||
|
require(matrices.isNotEmpty()) { "No matrices" }
|
||||||
|
val rowNum = matrices.first().rowNum
|
||||||
|
require(matrices.all { it.rowNum == rowNum }) { "All matrices must have the same number of rows: $rowNum" }
|
||||||
|
|
||||||
|
val columns = matrices.flatMap { it.columns }
|
||||||
|
|
||||||
|
return buildMatrix(rowNum, matrices.sumOf { it.colNum }) { row, column->
|
||||||
|
columns[column][row]
|
||||||
|
}
|
||||||
|
}
|
@ -228,7 +228,6 @@ public interface MutableStructureND<T> : StructureND<T> {
|
|||||||
* @param index the indices.
|
* @param index the indices.
|
||||||
* @param value the value.
|
* @param value the value.
|
||||||
*/
|
*/
|
||||||
@PerformancePitfall
|
|
||||||
public operator fun set(index: IntArray, value: T)
|
public operator fun set(index: IntArray, value: T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import space.kscience.kmath.expressions.Symbol
|
|||||||
import space.kscience.kmath.structures.MutableBufferFactory
|
import space.kscience.kmath.structures.MutableBufferFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An algebra for generic boolean logic
|
* Algebra for generic boolean logic
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public interface LogicAlgebra<T : Any> : Algebra<T> {
|
public interface LogicAlgebra<T : Any> : Algebra<T> {
|
||||||
|
@ -22,7 +22,7 @@ internal class BufferAccessor2D<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer<T> =
|
inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer<T> =
|
||||||
factory(rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) }
|
factory(this@BufferAccessor2D.rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) }
|
||||||
|
|
||||||
fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] }
|
fun create(mat: Structure2D<T>): MutableBuffer<T> = create { i, j -> mat[i, j] }
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class DoubleLUSolverTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDecomposition() = with(Double.algebra.linearSpace) {
|
fun testDecomposition() = with(Double.algebra.linearSpace) {
|
||||||
val matrix = matrix(2, 2)(
|
val matrix = MatrixBuilder(2, 2).fill(
|
||||||
3.0, 1.0,
|
3.0, 1.0,
|
||||||
2.0, 3.0
|
2.0, 3.0
|
||||||
)
|
)
|
||||||
@ -44,14 +44,14 @@ class DoubleLUSolverTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInvert() = Double.algebra.linearSpace.run {
|
fun testInvert() = Double.algebra.linearSpace.run {
|
||||||
val matrix = matrix(2, 2)(
|
val matrix = MatrixBuilder(2, 2).fill(
|
||||||
3.0, 1.0,
|
3.0, 1.0,
|
||||||
1.0, 3.0
|
1.0, 3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
val inverted = lupSolver().inverse(matrix)
|
val inverted = lupSolver().inverse(matrix)
|
||||||
|
|
||||||
val expected = matrix(2, 2)(
|
val expected = MatrixBuilder(2, 2).fill(
|
||||||
0.375, -0.125,
|
0.375, -0.125,
|
||||||
-0.125, 0.375
|
-0.125, 0.375
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2025 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.linear
|
||||||
|
|
||||||
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.nd.StructureND
|
||||||
|
import space.kscience.kmath.operations.algebra
|
||||||
|
import space.kscience.kmath.structures.Float64
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
@UnstableKMathAPI
|
||||||
|
class MatrixBuilderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun buildCompositeMatrix() = with(Float64.algebra.linearSpace) {
|
||||||
|
|
||||||
|
val matrix = vstack(
|
||||||
|
sparse(1, 5) { set(0, 4, 1.0) },
|
||||||
|
hstack(
|
||||||
|
sparse(4, 4).fill(
|
||||||
|
1.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 1.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0
|
||||||
|
),
|
||||||
|
sparse(4, 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
println(StructureND.toString(matrix))
|
||||||
|
|
||||||
|
assertEquals(1.0, matrix[0, 4])
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ class MatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuilder() = Double.algebra.linearSpace.run {
|
fun testBuilder() = Double.algebra.linearSpace.run {
|
||||||
val matrix = matrix(2, 3)(
|
val matrix = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
|
@ -28,7 +28,7 @@ class ParallelMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuilder() = Float64Field.linearSpace.parallel {
|
fun testBuilder() = Float64Field.linearSpace.parallel {
|
||||||
val matrix = matrix(2, 3)(
|
val matrix = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
|
@ -37,8 +37,8 @@ public fun realMatrix(rowNum: Int, colNum: Int, initializer: Float64Field.(i: In
|
|||||||
Double.algebra.linearSpace.buildMatrix(rowNum, colNum, initializer)
|
Double.algebra.linearSpace.buildMatrix(rowNum, colNum, initializer)
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder<Double, Float64Field> =
|
public fun MatrixBuilder(rowNum: Int, colNum: Int): MatrixBuilder<Double, Float64Field> =
|
||||||
Double.algebra.linearSpace.matrix(rowNum, colNum)
|
Double.algebra.linearSpace.MatrixBuilder(rowNum, colNum)
|
||||||
|
|
||||||
public fun Array<DoubleArray>.toMatrix(): RealMatrix {
|
public fun Array<DoubleArray>.toMatrix(): RealMatrix {
|
||||||
return Double.algebra.linearSpace.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] }
|
||||||
|
@ -7,8 +7,9 @@ package space.kscience.kmath.real
|
|||||||
|
|
||||||
import space.kscience.kmath.PerformancePitfall
|
import space.kscience.kmath.PerformancePitfall
|
||||||
import space.kscience.kmath.UnstableKMathAPI
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.linear.MatrixBuilder
|
||||||
|
import space.kscience.kmath.linear.fill
|
||||||
import space.kscience.kmath.linear.linearSpace
|
import space.kscience.kmath.linear.linearSpace
|
||||||
import space.kscience.kmath.linear.matrix
|
|
||||||
import space.kscience.kmath.nd.StructureND
|
import space.kscience.kmath.nd.StructureND
|
||||||
import space.kscience.kmath.operations.algebra
|
import space.kscience.kmath.operations.algebra
|
||||||
import space.kscience.kmath.testutils.contentEquals
|
import space.kscience.kmath.testutils.contentEquals
|
||||||
@ -43,11 +44,11 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeatStackVertical() {
|
fun testRepeatStackVertical() {
|
||||||
val matrix1 = realMatrix(2, 3)(
|
val matrix1 = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = realMatrix(6, 3)(
|
val matrix2 = MatrixBuilder(6, 3).fill(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0,
|
0.0, 1.0, 2.0,
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
@ -60,12 +61,12 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMatrixAndDouble() = Double.algebra.linearSpace.run {
|
fun testMatrixAndDouble() = Double.algebra.linearSpace.run {
|
||||||
val matrix1 = realMatrix(2, 3)(
|
val matrix1 = space.kscience.kmath.real.MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, 2.0
|
4.0, 6.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0
|
val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0
|
||||||
val expectedResult = matrix(2, 3)(
|
val expectedResult = this.MatrixBuilder(2, 3).fill(
|
||||||
0.75, -0.5, 3.25,
|
0.75, -0.5, 3.25,
|
||||||
4.5, 7.0, 2.0
|
4.5, 7.0, 2.0
|
||||||
)
|
)
|
||||||
@ -74,13 +75,13 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDoubleAndMatrix() {
|
fun testDoubleAndMatrix() {
|
||||||
val matrix1 = realMatrix(2, 3)(
|
val matrix1 = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, 2.0
|
4.0, 6.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = 20.0 - (10.0 + (5.0 * matrix1))
|
val matrix2 = 20.0 - (10.0 + (5.0 * matrix1))
|
||||||
//val matrix2 = 10.0 + (5.0 * matrix1)
|
//val matrix2 = 10.0 + (5.0 * matrix1)
|
||||||
val expectedResult = realMatrix(2, 3)(
|
val expectedResult = MatrixBuilder(2, 3).fill(
|
||||||
5.0, 10.0, -5.0,
|
5.0, 10.0, -5.0,
|
||||||
-10.0, -20.0, 0.0
|
-10.0, -20.0, 0.0
|
||||||
)
|
)
|
||||||
@ -89,15 +90,15 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSquareAndPower() {
|
fun testSquareAndPower() {
|
||||||
val matrix1 = realMatrix(2, 3)(
|
val matrix1 = MatrixBuilder(2, 3).fill(
|
||||||
-1.0, 0.0, 3.0,
|
-1.0, 0.0, 3.0,
|
||||||
4.0, -6.0, -2.0
|
4.0, -6.0, -2.0
|
||||||
)
|
)
|
||||||
val matrix2 = realMatrix(2, 3)(
|
val matrix2 = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 9.0,
|
1.0, 0.0, 9.0,
|
||||||
16.0, 36.0, 4.0
|
16.0, 36.0, 4.0
|
||||||
)
|
)
|
||||||
val matrix3 = realMatrix(2, 3)(
|
val matrix3 = MatrixBuilder(2, 3).fill(
|
||||||
-1.0, 0.0, 27.0,
|
-1.0, 0.0, 27.0,
|
||||||
64.0, -216.0, -8.0
|
64.0, -216.0, -8.0
|
||||||
)
|
)
|
||||||
@ -108,16 +109,16 @@ internal class DoubleMatrixTest {
|
|||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
@Test
|
@Test
|
||||||
fun testTwoMatrixOperations() {
|
fun testTwoMatrixOperations() {
|
||||||
val matrix1 = realMatrix(2, 3)(
|
val matrix1 = MatrixBuilder(2, 3).fill(
|
||||||
-1.0, 0.0, 3.0,
|
-1.0, 0.0, 3.0,
|
||||||
4.0, -6.0, 7.0
|
4.0, -6.0, 7.0
|
||||||
)
|
)
|
||||||
val matrix2 = realMatrix(2, 3)(
|
val matrix2 = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, -2.0
|
4.0, 6.0, -2.0
|
||||||
)
|
)
|
||||||
val result = matrix1 * matrix2 + matrix1 - matrix2
|
val result = matrix1 * matrix2 + matrix1 - matrix2
|
||||||
val expectedResult = realMatrix(2, 3)(
|
val expectedResult = MatrixBuilder(2, 3).fill(
|
||||||
-3.0, 0.0, 9.0,
|
-3.0, 0.0, 9.0,
|
||||||
16.0, -48.0, -5.0
|
16.0, -48.0, -5.0
|
||||||
)
|
)
|
||||||
@ -126,16 +127,16 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testColumnOperations() {
|
fun testColumnOperations() {
|
||||||
val matrix1 = realMatrix(2, 4)(
|
val matrix1 = MatrixBuilder(2, 4).fill(
|
||||||
-1.0, 0.0, 3.0, 15.0,
|
-1.0, 0.0, 3.0, 15.0,
|
||||||
4.0, -6.0, 7.0, -11.0
|
4.0, -6.0, 7.0, -11.0
|
||||||
)
|
)
|
||||||
val matrix2 = realMatrix(2, 5)(
|
val matrix2 = MatrixBuilder(2, 5).fill(
|
||||||
-1.0, 0.0, 3.0, 15.0, -1.0,
|
-1.0, 0.0, 3.0, 15.0, -1.0,
|
||||||
4.0, -6.0, 7.0, -11.0, 4.0
|
4.0, -6.0, 7.0, -11.0, 4.0
|
||||||
)
|
)
|
||||||
val col1 = realMatrix(2, 1)(0.0, -6.0)
|
val col1 = MatrixBuilder(2, 1).fill(0.0, -6.0)
|
||||||
val cols1to2 = realMatrix(2, 2)(
|
val cols1to2 = MatrixBuilder(2, 2).fill(
|
||||||
0.0, 3.0,
|
0.0, 3.0,
|
||||||
-6.0, 7.0
|
-6.0, 7.0
|
||||||
)
|
)
|
||||||
@ -160,7 +161,7 @@ internal class DoubleMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAllElementOperations() = Double.algebra.linearSpace.run {
|
fun testAllElementOperations() = Double.algebra.linearSpace.run {
|
||||||
val matrix1 = matrix(2, 4)(
|
val matrix1 = this.MatrixBuilder(2, 4).fill(
|
||||||
-1.0, 0.0, 3.0, 15.0,
|
-1.0, 0.0, 3.0, 15.0,
|
||||||
4.0, -6.0, 7.0, -11.0
|
4.0, -6.0, 7.0, -11.0
|
||||||
)
|
)
|
||||||
|
@ -8,10 +8,7 @@ package space.kscience.kmath.geometry.euclidean3d
|
|||||||
import space.kscience.kmath.UnstableKMathAPI
|
import space.kscience.kmath.UnstableKMathAPI
|
||||||
import space.kscience.kmath.complex.*
|
import space.kscience.kmath.complex.*
|
||||||
import space.kscience.kmath.geometry.*
|
import space.kscience.kmath.geometry.*
|
||||||
import space.kscience.kmath.linear.LinearSpace
|
import space.kscience.kmath.linear.*
|
||||||
import space.kscience.kmath.linear.Matrix
|
|
||||||
import space.kscience.kmath.linear.linearSpace
|
|
||||||
import space.kscience.kmath.linear.matrix
|
|
||||||
import space.kscience.kmath.operations.Float64Field
|
import space.kscience.kmath.operations.Float64Field
|
||||||
import space.kscience.kmath.structures.Float64
|
import space.kscience.kmath.structures.Float64
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
@ -103,7 +100,7 @@ public fun Quaternion.toRotationMatrix(
|
|||||||
linearSpace: LinearSpace<Double, *> = Float64Field.linearSpace,
|
linearSpace: LinearSpace<Double, *> = Float64Field.linearSpace,
|
||||||
): Matrix<Float64> {
|
): Matrix<Float64> {
|
||||||
val s = QuaternionAlgebra.norm(this).pow(-2)
|
val s = QuaternionAlgebra.norm(this).pow(-2)
|
||||||
return linearSpace.matrix(3, 3)(
|
return linearSpace.MatrixBuilder(3, 3).fill(
|
||||||
1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w),
|
1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w),
|
||||||
2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w),
|
2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w),
|
||||||
2 * s * (x * z - y * w), 2 * s * (y * z + x * w), 1.0 - 2 * s * (x * x + y * y)
|
2 * s * (x * z - y * w), 2 * s * (y * z + x * w), 1.0 - 2 * s * (x * x + y * y)
|
||||||
|
@ -15,6 +15,10 @@ kscience {
|
|||||||
// api(projects.kmathFunctions)
|
// api(projects.kmathFunctions)
|
||||||
api(libs.ojalgo)
|
api(libs.ojalgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jvmTest{
|
||||||
|
implementation(projects.testUtils)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readme {
|
readme {
|
||||||
|
@ -30,7 +30,7 @@ class OjalgoMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuilder() = Ojalgo.Companion.R064.linearSpace {
|
fun testBuilder() = Ojalgo.Companion.R064.linearSpace {
|
||||||
val matrix = buildMatrix(2, 3)(
|
val matrix = MatrixBuilder(2, 3).fill(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
@ -77,7 +77,7 @@ class OjalgoMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCholesky() = with(Ojalgo.Companion.R064.linearSpace) {
|
fun testCholesky() = with(Ojalgo.Companion.R064.linearSpace) {
|
||||||
val l = buildMatrix(4, 4)(
|
val l = MatrixBuilder(4, 4).fill(
|
||||||
1.0, 0.0, 0.0, 0.0,
|
1.0, 0.0, 0.0, 0.0,
|
||||||
1.0, 1.0, 0.0, 0.0,
|
1.0, 1.0, 0.0, 0.0,
|
||||||
1.0, 1.0, 1.0, 0.0,
|
1.0, 1.0, 1.0, 0.0,
|
||||||
|
@ -91,7 +91,7 @@ public object QowOptimizer : Optimizer<Double, XYFit> {
|
|||||||
* D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2
|
* D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2
|
||||||
*/
|
*/
|
||||||
private fun QoWeight.covarF(): Matrix<Float64> =
|
private fun QoWeight.covarF(): Matrix<Float64> =
|
||||||
linearSpace.matrix(size, size).symmetric { s1, s2 ->
|
linearSpace.MatrixBuilder(size, size).symmetric { s1, s2 ->
|
||||||
(0 until data.size).sumOf { d -> derivs[d, s1] * derivs[d, s2] / dispersion[d] }
|
(0 until data.size).sumOf { d -> derivs[d, s1] * derivs[d, s2] / dispersion[d] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user