Add sparse matrix builder

This commit is contained in:
Alexander Nozik 2025-01-26 22:17:36 +03:00
parent 676cf5ff3b
commit c70a7c5128
22 changed files with 316 additions and 94 deletions
CHANGELOG.md
benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks
build.gradle.kts
examples/src/main/kotlin/space/kscience/kmath/operations
kmath-core/src
commonMain/kotlin/space/kscience/kmath
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
build.gradle.kts
src/jvmTest/kotlin/space/kscience/kmath/ojalgo
kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization

@ -4,6 +4,7 @@
### Added
- Convenient matrix builders for rows, columns, vstacks and hstacks
- Spreadsheet matrix builder
### Changed

@ -9,8 +9,8 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import space.kscience.kmath.linear.MatrixBuilder
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.linear.symmetric
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.tensors.core.symEigJacobi
@ -24,7 +24,7 @@ internal class TensorAlgebraBenchmark {
private val random = Random(12224)
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

@ -14,7 +14,7 @@ allprojects {
}
group = "space.kscience"
version = "0.4.1"
version = "0.4.2-dev"
}
dependencies{

@ -6,22 +6,21 @@
package space.kscience.kmath.operations
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.Structure2D
import space.kscience.kmath.nd.mutableStructureND
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.structures.Float64
import space.kscience.kmath.viktor.viktorAlgebra
import kotlin.collections.component1
import kotlin.collections.component2
fun main() {
val viktorStructure = Float64Field.viktorAlgebra.mutableStructureND(2, 2) { (i, j) ->
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 {
exp(viktorStructure) + 2.0 * cmMatrix

@ -5,7 +5,7 @@
package space.kscience.kmath.linear
import space.kscience.attributes.FlagAttribute
import space.kscience.attributes.Attributes
import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
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.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 rows: Int,
public val columns: Int,
public val rowNum: Int,
public val colNum: Int,
) : WithType<T> {
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
*/
@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)
@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
* full `size^2` number of elements, but caches elements during calls to save [builder] calls. [builder] is always called in the
* upper triangle region meaning that `i <= j`
* Naive implementation of a symmetric matrix builder, that adds a [Symmetric] tag.
* The resulting matrix contains full `size^2` number of elements,
* 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(
builder: (i: Int, j: Int) -> T,
@UnstableKMathAPI
public fun <T, A : Ring<T>> MatrixBuilder<T, A>.symmetric(
builder: A.(i: Int, j: Int) -> T,
): Matrix<T> {
require(columns == rows) { "In order to build symmetric matrix, number of rows $rows should be equal to number of columns $columns" }
return with(BufferAccessor2D<T?>(rows, rows, MutableBufferFactory(type))) {
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?>(rowNum, rowNum, MutableBufferFactory(type))) {
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 cached = cache[index]
if (cached == null) {
@ -81,4 +70,50 @@ public fun <T : Any, A : Ring<T>> MatrixBuilder<T, A>.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
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,
public val generator: (i: Int, j: Int) -> T,
) : Matrix<T> {
override val shape: ShapeND get() = ShapeND(rowNum, colNum)
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>
/**
* 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.
*/
public interface IsDiagonal : MatrixAttribute<Unit>, FlagAttribute {
public interface IsDiagonal : Symmetric {
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 value the value.
*/
@PerformancePitfall
public operator fun set(index: IntArray, value: T)
}

@ -10,7 +10,7 @@ import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.structures.MutableBufferFactory
/**
* An algebra for generic boolean logic
* Algebra for generic boolean logic
*/
@UnstableKMathAPI
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> =
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] }

@ -29,7 +29,7 @@ class DoubleLUSolverTest {
@Test
fun testDecomposition() = with(Double.algebra.linearSpace) {
val matrix = matrix(2, 2)(
val matrix = MatrixBuilder(2, 2).fill(
3.0, 1.0,
2.0, 3.0
)
@ -44,14 +44,14 @@ class DoubleLUSolverTest {
@Test
fun testInvert() = Double.algebra.linearSpace.run {
val matrix = matrix(2, 2)(
val matrix = MatrixBuilder(2, 2).fill(
3.0, 1.0,
1.0, 3.0
)
val inverted = lupSolver().inverse(matrix)
val expected = matrix(2, 2)(
val expected = MatrixBuilder(2, 2).fill(
0.375, -0.125,
-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
fun testBuilder() = Double.algebra.linearSpace.run {
val matrix = matrix(2, 3)(
val matrix = MatrixBuilder(2, 3).fill(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0
)

@ -28,7 +28,7 @@ class ParallelMatrixTest {
@Test
fun testBuilder() = Float64Field.linearSpace.parallel {
val matrix = matrix(2, 3)(
val matrix = MatrixBuilder(2, 3).fill(
1.0, 0.0, 0.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)
@OptIn(UnstableKMathAPI::class)
public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder<Double, Float64Field> =
Double.algebra.linearSpace.matrix(rowNum, colNum)
public fun MatrixBuilder(rowNum: Int, colNum: Int): MatrixBuilder<Double, Float64Field> =
Double.algebra.linearSpace.MatrixBuilder(rowNum, colNum)
public fun Array<DoubleArray>.toMatrix(): RealMatrix {
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.UnstableKMathAPI
import space.kscience.kmath.linear.MatrixBuilder
import space.kscience.kmath.linear.fill
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.algebra
import space.kscience.kmath.testutils.contentEquals
@ -43,11 +44,11 @@ internal class DoubleMatrixTest {
@Test
fun testRepeatStackVertical() {
val matrix1 = realMatrix(2, 3)(
val matrix1 = MatrixBuilder(2, 3).fill(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0
)
val matrix2 = realMatrix(6, 3)(
val matrix2 = MatrixBuilder(6, 3).fill(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0,
1.0, 0.0, 0.0,
@ -60,12 +61,12 @@ internal class DoubleMatrixTest {
@Test
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,
4.0, 6.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,
4.5, 7.0, 2.0
)
@ -74,13 +75,13 @@ internal class DoubleMatrixTest {
@Test
fun testDoubleAndMatrix() {
val matrix1 = realMatrix(2, 3)(
val matrix1 = MatrixBuilder(2, 3).fill(
1.0, 0.0, 3.0,
4.0, 6.0, 2.0
)
val matrix2 = 20.0 - (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,
-10.0, -20.0, 0.0
)
@ -89,15 +90,15 @@ internal class DoubleMatrixTest {
@Test
fun testSquareAndPower() {
val matrix1 = realMatrix(2, 3)(
val matrix1 = MatrixBuilder(2, 3).fill(
-1.0, 0.0, 3.0,
4.0, -6.0, -2.0
)
val matrix2 = realMatrix(2, 3)(
val matrix2 = MatrixBuilder(2, 3).fill(
1.0, 0.0, 9.0,
16.0, 36.0, 4.0
)
val matrix3 = realMatrix(2, 3)(
val matrix3 = MatrixBuilder(2, 3).fill(
-1.0, 0.0, 27.0,
64.0, -216.0, -8.0
)
@ -108,16 +109,16 @@ internal class DoubleMatrixTest {
@OptIn(UnstableKMathAPI::class)
@Test
fun testTwoMatrixOperations() {
val matrix1 = realMatrix(2, 3)(
val matrix1 = MatrixBuilder(2, 3).fill(
-1.0, 0.0, 3.0,
4.0, -6.0, 7.0
)
val matrix2 = realMatrix(2, 3)(
val matrix2 = MatrixBuilder(2, 3).fill(
1.0, 0.0, 3.0,
4.0, 6.0, -2.0
)
val result = matrix1 * matrix2 + matrix1 - matrix2
val expectedResult = realMatrix(2, 3)(
val expectedResult = MatrixBuilder(2, 3).fill(
-3.0, 0.0, 9.0,
16.0, -48.0, -5.0
)
@ -126,16 +127,16 @@ internal class DoubleMatrixTest {
@Test
fun testColumnOperations() {
val matrix1 = realMatrix(2, 4)(
val matrix1 = MatrixBuilder(2, 4).fill(
-1.0, 0.0, 3.0, 15.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,
4.0, -6.0, 7.0, -11.0, 4.0
)
val col1 = realMatrix(2, 1)(0.0, -6.0)
val cols1to2 = realMatrix(2, 2)(
val col1 = MatrixBuilder(2, 1).fill(0.0, -6.0)
val cols1to2 = MatrixBuilder(2, 2).fill(
0.0, 3.0,
-6.0, 7.0
)
@ -160,7 +161,7 @@ internal class DoubleMatrixTest {
@Test
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,
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.complex.*
import space.kscience.kmath.geometry.*
import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.linear.matrix
import space.kscience.kmath.linear.*
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.Float64
import kotlin.math.*
@ -103,7 +100,7 @@ public fun Quaternion.toRotationMatrix(
linearSpace: LinearSpace<Double, *> = Float64Field.linearSpace,
): Matrix<Float64> {
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),
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)

@ -15,6 +15,10 @@ kscience {
// api(projects.kmathFunctions)
api(libs.ojalgo)
}
jvmTest{
implementation(projects.testUtils)
}
}
readme {

@ -30,7 +30,7 @@ class OjalgoMatrixTest {
@Test
fun testBuilder() = Ojalgo.Companion.R064.linearSpace {
val matrix = buildMatrix(2, 3)(
val matrix = MatrixBuilder(2, 3).fill(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0
)
@ -77,7 +77,7 @@ class OjalgoMatrixTest {
@Test
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, 1.0, 0.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
*/
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] }
}