Add ojalgo module

This commit is contained in:
Alexander Nozik 2025-01-12 11:40:51 +03:00
parent da9608128b
commit d8af4e36ed
28 changed files with 440 additions and 137 deletions

View File

@ -4,15 +4,19 @@
### Added ### Added
- Metropolis-Hastings sampler - Metropolis-Hastings sampler
- Ojalgo `LinearSpace` implementation.
### Changed ### Changed
- attributes-kt moved to a separate project, and the version used is 0.3.0
- Kotlin 2.1. Now use cross-compilation to deploy macOS targets.
- Changed `origin` to `cmMatrix` in kmath-commons to avoid property name clash. Expose bidirectional conversion in `CMLinearSpace`
### Deprecated ### Deprecated
### Removed ### Removed
### Fixed ### Fixed
- Fix EJML to properly treat vectors as columns - (BREAKING CHANGE) Fix EJML to properly treat vectors as columns
### Security ### Security

View File

@ -53,7 +53,6 @@ kotlin {
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation(project(":kmath-for-real")) implementation(project(":kmath-for-real"))
implementation(project(":kmath-tensors")) implementation(project(":kmath-tensors"))
implementation(project(":kmath-multik"))
implementation(libs.multik.default) implementation(libs.multik.default)
implementation(spclibs.kotlinx.benchmark.runtime) implementation(spclibs.kotlinx.benchmark.runtime)
} }
@ -61,13 +60,14 @@ kotlin {
val jvmMain by getting { val jvmMain by getting {
dependencies { dependencies {
implementation(project(":kmath-commons")) implementation(projects.kmathCommons)
implementation(project(":kmath-ejml")) implementation(projects.kmathEjml)
implementation(project(":kmath-nd4j")) implementation(projects.kmathNd4j)
implementation(project(":kmath-kotlingrad")) implementation(projects.kmathKotlingrad)
implementation(project(":kmath-viktor")) implementation(projects.kmathViktor)
// implementation(project(":kmath-jafama")) implementation(projects.kmathOjalgo)
implementation(projects.kmath.kmathTensorflow) implementation(projects.kmath.kmathTensorflow)
implementation(projects.kmathMultik)
implementation("org.tensorflow:tensorflow-core-platform:0.4.0") implementation("org.tensorflow:tensorflow-core-platform:0.4.0")
implementation("org.nd4j:nd4j-native:1.0.0-M1") implementation("org.nd4j:nd4j-native:1.0.0-M1")
// uncomment if your system supports AVX2 // uncomment if your system supports AVX2

View File

@ -10,10 +10,13 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.commons.linear.CMLinearSpace.dot
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.Float64ParallelLinearSpace import space.kscience.kmath.linear.Float64ParallelLinearSpace
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.ojalgo.Ojalgo
import space.kscience.kmath.ojalgo.linearSpace
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.tensorflow.produceWithTF import space.kscience.kmath.tensorflow.produceWithTF
import space.kscience.kmath.tensors.core.tensorAlgebra import space.kscience.kmath.tensors.core.tensorAlgebra
@ -70,6 +73,11 @@ internal class DotBenchmark {
blackhole.consume(matrix1 dot matrix2) blackhole.consume(matrix1 dot matrix2)
} }
@Benchmark
fun ojalgoDot(blackhole: Blackhole) = Ojalgo.R064.linearSpace {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark @Benchmark
fun multikDot(blackhole: Blackhole) = with(multikAlgebra) { fun multikDot(blackhole: Blackhole) = with(multikAlgebra) {
blackhole.consume(matrix1 dot matrix2) blackhole.consume(matrix1 dot matrix2)

View File

@ -12,10 +12,9 @@ import kotlinx.benchmark.State
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.commons.linear.lupSolver import space.kscience.kmath.commons.linear.lupSolver
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.*
import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.ojalgo.Ojalgo
import space.kscience.kmath.linear.lupSolver import space.kscience.kmath.ojalgo.linearSpace
import space.kscience.kmath.linear.parallel
import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.algebra
import kotlin.random.Random import kotlin.random.Random
@ -48,10 +47,14 @@ internal class MatrixInverseBenchmark {
blackhole.consume(lupSolver().inverse(matrix)) blackhole.consume(lupSolver().inverse(matrix))
} }
@Benchmark @Benchmark
fun ejmlInverse(blackhole: Blackhole) = EjmlLinearSpaceDDRM { fun ejmlInverse(blackhole: Blackhole) = EjmlLinearSpaceDDRM {
blackhole.consume(matrix.toEjml().inverted()) blackhole.consume(matrix.inverted())
}
@Benchmark
fun ojalgoInverse(blackhole: Blackhole) = Ojalgo.R064.linearSpace {
blackhole.consume(matrix.getOrComputeAttribute(Inverted))
} }
} }

View File

@ -6,8 +6,6 @@ plugins {
alias(spclibs.plugins.kotlinx.kover) alias(spclibs.plugins.kotlinx.kover)
} }
val attributesVersion by extra("0.2.0")
allprojects { allprojects {
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
@ -72,7 +70,7 @@ ksciencePublish {
useSPCTeam() useSPCTeam()
} }
repository("spc", "https://maven.sciprog.center/kscience") repository("spc", "https://maven.sciprog.center/kscience")
sonatype("https://oss.sonatype.org") central()
} }
apiValidation.nonPublicMarkers.add("space.kscience.kmath.UnstableKMathAPI") apiValidation.nonPublicMarkers.add("space.kscience.kmath.UnstableKMathAPI")

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
@ -12,6 +13,7 @@ import space.kscience.kmath.operations.algebra
import space.kscience.kmath.structures.Float64 import space.kscience.kmath.structures.Float64
import kotlin.random.Random import kotlin.random.Random
@OptIn(PerformancePitfall::class)
fun main() { fun main() {
val dim = 46 val dim = 46
@ -21,7 +23,7 @@ fun main() {
listOf(CMLinearSpace, EjmlLinearSpaceDDRM).forEach { algebra -> listOf(CMLinearSpace, EjmlLinearSpaceDDRM).forEach { algebra ->
with(algebra) { with(algebra) {
//create a simmetric matrix //create a symmetric matrix
val matrix = buildMatrix(dim, dim) { row, col -> val matrix = buildMatrix(dim, dim) { row, col ->
if (row >= col) u[row, col] else u[col, row] if (row >= col) u[row, col] else u[col, row]
} }

View File

@ -10,7 +10,7 @@ org.gradle.workers.max=4
kotlin.code.style=official kotlin.code.style=official
kotlin.mpp.stability.nowarn=true kotlin.mpp.stability.nowarn=true
kotlin.native.ignoreDisabledTargets=true kotlin.native.ignoreDisabledTargets=true
org.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
kotlin.native.enableKlibsCrossCompilation=true kotlin.native.enableKlibsCrossCompilation=true
toolsVersion=0.16.0-kotlin-2.1.0 toolsVersion=0.16.1-kotlin-2.1.0

View File

@ -4,6 +4,9 @@ commons-rng = "1.6"
multik = "0.2.3" multik = "0.2.3"
[libraries] [libraries]
attributes = "space.kscience:attributes-kt:0.3.0"
commons-math = "org.apache.commons:commons-math3:3.6.1"
commons-rng-simple = { module = "org.apache.commons:commons-rng-simple", version.ref = "commons-rng" } commons-rng-simple = { module = "org.apache.commons:commons-rng-simple", version.ref = "commons-rng" }
commons-rng-sampling = { module = "org.apache.commons:commons-rng-sampling", version.ref = "commons-rng" } commons-rng-sampling = { module = "org.apache.commons:commons-rng-sampling", version.ref = "commons-rng" }
@ -11,4 +14,6 @@ commons-rng-sampling = { module = "org.apache.commons:commons-rng-sampling", ver
multik-core = { module = "org.jetbrains.kotlinx:multik-core", version.ref = "multik" } multik-core = { module = "org.jetbrains.kotlinx:multik-core", version.ref = "multik" }
multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref = "multik" } multik-default = { module = "org.jetbrains.kotlinx:multik-default", version.ref = "multik" }
ojalgo = "org.ojalgo:ojalgo:55.1.0"
[plugins] [plugins]

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -22,7 +22,7 @@ import space.kscience.kmath.wasm.compile as wasmCompile
import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
private object WasmCompilerTestContext : CompilerTestContext { internal object WasmCompilerTestContext : CompilerTestContext {
override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> = wasmCompileToExpression(algebra) override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> = wasmCompileToExpression(algebra)
override fun MST.compile(algebra: Int32Ring, arguments: Map<Symbol, Int>): Int = wasmCompile(algebra, arguments) override fun MST.compile(algebra: Int32Ring, arguments: Map<Symbol, Int>): Int = wasmCompile(algebra, arguments)
override fun MST.compileToExpression(algebra: Float64Field): Expression<Float64> = wasmCompileToExpression(algebra) override fun MST.compileToExpression(algebra: Float64Field): Expression<Float64> = wasmCompileToExpression(algebra)
@ -31,7 +31,7 @@ private object WasmCompilerTestContext : CompilerTestContext {
wasmCompile(algebra, arguments) wasmCompile(algebra, arguments)
} }
private object ESTreeCompilerTestContext : CompilerTestContext { internal object ESTreeCompilerTestContext : CompilerTestContext {
override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> = estreeCompileToExpression(algebra) override fun MST.compileToExpression(algebra: Int32Ring): Expression<Int> = estreeCompileToExpression(algebra)
override fun MST.compile(algebra: Int32Ring, arguments: Map<Symbol, Int>): Int = estreeCompile(algebra, arguments) override fun MST.compile(algebra: Int32Ring, arguments: Map<Symbol, Int>): Int = estreeCompile(algebra, arguments)
override fun MST.compileToExpression(algebra: Float64Field): Expression<Float64> = estreeCompileToExpression(algebra) override fun MST.compileToExpression(algebra: Float64Field): Expression<Float64> = estreeCompileToExpression(algebra)

View File

@ -13,7 +13,7 @@ kscience {
api(projects.kmathOptimization) api(projects.kmathOptimization)
api(projects.kmathStat) api(projects.kmathStat)
api(projects.kmathFunctions) api(projects.kmathFunctions)
api("org.apache.commons:commons-math3:3.6.1") api(libs.commons.math)
} }
} }

View File

@ -6,8 +6,6 @@
package space.kscience.kmath.commons.linear package space.kscience.kmath.commons.linear
import org.apache.commons.math3.linear.* import org.apache.commons.math3.linear.*
import org.apache.commons.math3.linear.LUDecomposition
import org.apache.commons.math3.linear.SingularValueDecomposition
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.linear.* import space.kscience.kmath.linear.*
@ -23,27 +21,26 @@ import space.kscience.kmath.structures.Float64
import space.kscience.kmath.structures.IntBuffer import space.kscience.kmath.structures.IntBuffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
public class CMMatrix(public val origin: RealMatrix) : Matrix<Float64> { @JvmInline
public value class CMMatrix(public val cmMatrix: RealMatrix) : Matrix<Float64> {
override val rowNum: Int get() = origin.rowDimension override val rowNum: Int get() = cmMatrix.rowDimension
override val colNum: Int get() = origin.columnDimension override val colNum: Int get() = cmMatrix.columnDimension
override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) override operator fun get(i: Int, j: Int): Double = cmMatrix.getEntry(i, j)
} }
@JvmInline @JvmInline
public value class CMVector(public val origin: RealVector) : Point<Float64> { public value class CMVector(public val cmVector: RealVector) : Point<Float64> {
override val size: Int get() = origin.dimension override val size: Int get() = cmVector.dimension
override operator fun get(index: Int): Double = origin.getEntry(index) override operator fun get(index: Int): Double = cmVector.getEntry(index)
override operator fun iterator(): Iterator<Float64> = origin.toArray().iterator() override operator fun iterator(): Iterator<Float64> = cmVector.toArray().iterator()
override fun toString(): String = Buffer.toString(this) override fun toString(): String = Buffer.toString(this)
} }
public fun RealVector.toPoint(): CMVector = CMVector(this)
public object CMLinearSpace : LinearSpace<Double, Float64Field> { public object CMLinearSpace : LinearSpace<Double, Float64Field> {
override val elementAlgebra: Float64Field get() = Float64Field override val elementAlgebra: Float64Field get() = Float64Field
@ -59,52 +56,52 @@ public object CMLinearSpace : LinearSpace<Double, Float64Field> {
} }
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public fun Matrix<Float64>.toCM(): CMMatrix = when (val matrix = origin) { public fun Matrix<Float64>.toCM(): RealMatrix = when (val matrix = origin) {
is CMMatrix -> matrix is CMMatrix -> matrix.cmMatrix
else -> { else -> {
//TODO add feature analysis //TODO add feature analysis
val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } } val array = Array(rowNum) { i -> DoubleArray(colNum) { j -> get(i, j) } }
Array2DRowRealMatrix(array).wrap() Array2DRowRealMatrix(array)
} }
} }
public fun Point<Float64>.toCM(): CMVector = if (this is CMVector) this else { public fun Point<Float64>.toCM(): RealVector = if (this is CMVector) cmVector else {
val array = DoubleArray(size) { this[it] } val array = DoubleArray(size) { get(it) }
ArrayRealVector(array).wrap() ArrayRealVector(array)
} }
internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this) public fun RealMatrix.asMatrix(): CMMatrix = CMMatrix(this)
internal fun RealVector.wrap(): CMVector = CMVector(this) public fun RealVector.asVector(): CMVector = CMVector(this)
override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Point<Float64> = override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Point<Float64> =
ArrayRealVector(DoubleArray(size) { Float64Field.initializer(it) }).wrap() ArrayRealVector(DoubleArray(size) { Float64Field.initializer(it) }).asVector()
override fun Matrix<Float64>.plus(other: Matrix<Float64>): CMMatrix = override fun Matrix<Float64>.plus(other: Matrix<Float64>): CMMatrix =
toCM().origin.add(other.toCM().origin).wrap() toCM().add(other.toCM()).asMatrix()
override fun Point<Float64>.plus(other: Point<Float64>): CMVector = override fun Point<Float64>.plus(other: Point<Float64>): CMVector =
toCM().origin.add(other.toCM().origin).wrap() toCM().add(other.toCM()).asVector()
override fun Point<Float64>.minus(other: Point<Float64>): CMVector = override fun Point<Float64>.minus(other: Point<Float64>): CMVector =
toCM().origin.subtract(other.toCM().origin).wrap() toCM().subtract(other.toCM()).asVector()
override fun Matrix<Float64>.dot(other: Matrix<Float64>): CMMatrix = override fun Matrix<Float64>.dot(other: Matrix<Float64>): CMMatrix =
toCM().origin.multiply(other.toCM().origin).wrap() toCM().multiply(other.toCM()).asMatrix()
override fun Matrix<Float64>.dot(vector: Point<Float64>): CMVector = override fun Matrix<Float64>.dot(vector: Point<Float64>): CMVector =
toCM().origin.preMultiply(vector.toCM().origin).wrap() toCM().preMultiply(vector.toCM()).asVector()
override operator fun Matrix<Float64>.minus(other: Matrix<Float64>): CMMatrix = override operator fun Matrix<Float64>.minus(other: Matrix<Float64>): CMMatrix =
toCM().origin.subtract(other.toCM().origin).wrap() toCM().subtract(other.toCM()).asMatrix()
override operator fun Matrix<Float64>.times(value: Double): CMMatrix = override operator fun Matrix<Float64>.times(value: Double): CMMatrix =
toCM().origin.scalarMultiply(value).wrap() toCM().scalarMultiply(value).asMatrix()
override fun Double.times(m: Matrix<Float64>): CMMatrix = override fun Double.times(m: Matrix<Float64>): CMMatrix =
m * this m * this
override fun Point<Float64>.times(value: Double): CMVector = override fun Point<Float64>.times(value: Double): CMVector =
toCM().origin.mapMultiply(value).wrap() toCM().mapMultiply(value).asVector()
override fun Double.times(v: Point<Float64>): CMVector = override fun Double.times(v: Point<Float64>): CMVector =
v * this v * this
@ -112,36 +109,38 @@ public object CMLinearSpace : LinearSpace<Double, Float64Field> {
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
override fun <V, A : StructureAttribute<V>> computeAttribute(structure: Structure2D<Float64>, attribute: A): V? { override fun <V, A : StructureAttribute<V>> computeAttribute(structure: Structure2D<Float64>, attribute: A): V? {
val origin = structure.toCM().origin val origin = structure.toCM()
val raw: Any? = when (attribute) { val raw: Any? = when (attribute) {
IsDiagonal -> if (origin is DiagonalMatrix) Unit else null IsDiagonal -> if (origin is DiagonalMatrix) Unit else null
Determinant -> LUDecomposition(origin).determinant Determinant -> org.apache.commons.math3.linear.LUDecomposition(origin).determinant
Inverted -> org.apache.commons.math3.linear.LUDecomposition(origin).solver.inverse.asMatrix()
LUP -> object : LupDecomposition<Float64> { LUP -> object : LupDecomposition<Float64> {
val lup by lazy { LUDecomposition(origin) } val lup by lazy { org.apache.commons.math3.linear.LUDecomposition(origin) }
override val pivot: IntBuffer get() = lup.pivot.asBuffer() override val pivot: IntBuffer get() = lup.pivot.asBuffer()
override val l: Matrix<Float64> get() = lup.l.wrap() override val l: Matrix<Float64> get() = lup.l.asMatrix().withAttribute(LowerTriangular)
override val u: Matrix<Float64> get() = lup.u.wrap() override val u: Matrix<Float64> get() = lup.u.asMatrix().withAttribute(UpperTriangular)
} }
Cholesky -> object : CholeskyDecomposition<Float64> { Cholesky -> object : CholeskyDecomposition<Float64> {
val cmCholesky by lazy { org.apache.commons.math3.linear.CholeskyDecomposition(origin) } val cmCholesky by lazy { org.apache.commons.math3.linear.CholeskyDecomposition(origin) }
override val l: Matrix<Float64> get() = cmCholesky.l.wrap() override val l: Matrix<Float64> get() = cmCholesky.l.asMatrix()
} }
QR -> object : QRDecomposition<Float64> { QR -> object : QRDecomposition<Float64> {
val cmQr by lazy { org.apache.commons.math3.linear.QRDecomposition(origin) } val cmQr by lazy { org.apache.commons.math3.linear.QRDecomposition(origin) }
override val q: Matrix<Float64> get() = cmQr.q.wrap().withAttribute(OrthogonalAttribute) override val q: Matrix<Float64> get() = cmQr.q.asMatrix().withAttribute(OrthogonalAttribute)
override val r: Matrix<Float64> get() = cmQr.r.wrap().withAttribute(UpperTriangular) override val r: Matrix<Float64> get() = cmQr.r.asMatrix().withAttribute(UpperTriangular)
} }
SVD -> object : space.kscience.kmath.linear.SingularValueDecomposition<Float64> { SVD -> object : space.kscience.kmath.linear.SingularValueDecomposition<Float64> {
val cmSvd by lazy { SingularValueDecomposition(origin) } val cmSvd by lazy { org.apache.commons.math3.linear.SingularValueDecomposition(origin) }
override val u: Matrix<Float64> get() = cmSvd.u.wrap() override val u: Matrix<Float64> get() = cmSvd.u.asMatrix()
override val s: Matrix<Float64> get() = cmSvd.s.wrap() override val s: Matrix<Float64> get() = cmSvd.s.asMatrix()
override val v: Matrix<Float64> get() = cmSvd.v.wrap() override val v: Matrix<Float64> get() = cmSvd.v.asMatrix()
override val singularValues: Point<Float64> get() = cmSvd.singularValues.asBuffer() override val singularValues: Point<Float64> get() = cmSvd.singularValues.asBuffer()
} }
@ -149,8 +148,8 @@ public object CMLinearSpace : LinearSpace<Double, Float64Field> {
EIG -> object : EigenDecomposition<Float64> { EIG -> object : EigenDecomposition<Float64> {
val cmEigen by lazy { org.apache.commons.math3.linear.EigenDecomposition(origin) } val cmEigen by lazy { org.apache.commons.math3.linear.EigenDecomposition(origin) }
override val v: Matrix<Float64> get() = cmEigen.v.wrap() override val v: Matrix<Float64> get() = cmEigen.v.asMatrix()
override val d: Matrix<Float64> get() = cmEigen.d.wrap() override val d: Matrix<Float64> get() = cmEigen.d.asMatrix()
} }
else -> null else -> null
@ -161,8 +160,8 @@ public object CMLinearSpace : LinearSpace<Double, Float64Field> {
} }
public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(origin.add(other.origin)) public operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(cmMatrix.add(other.cmMatrix))
public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(origin.subtract(other.origin)) public operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(cmMatrix.subtract(other.cmMatrix))
public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = CMMatrix(origin.multiply(other.origin)) public infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = CMMatrix(cmMatrix.multiply(other.cmMatrix))

View File

@ -19,43 +19,44 @@ public enum class CMDecomposition {
CHOLESKY CHOLESKY
} }
private fun CMLinearSpace.solver( private fun CMLinearSpace.cmSolver(
a: Matrix<Float64>, a: Matrix<Float64>,
decomposition: CMDecomposition = CMDecomposition.LUP, decomposition: CMDecomposition = CMDecomposition.LUP,
): DecompositionSolver = when (decomposition) { ): DecompositionSolver = when (decomposition) {
CMDecomposition.LUP -> LUDecomposition(a.toCM().origin).solver CMDecomposition.LUP -> LUDecomposition(a.toCM()).solver
CMDecomposition.RRQR -> RRQRDecomposition(a.toCM().origin).solver CMDecomposition.RRQR -> RRQRDecomposition(a.toCM()).solver
CMDecomposition.QR -> QRDecomposition(a.toCM().origin).solver CMDecomposition.QR -> QRDecomposition(a.toCM()).solver
CMDecomposition.EIGEN -> EigenDecomposition(a.toCM().origin).solver CMDecomposition.EIGEN -> EigenDecomposition(a.toCM()).solver
CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM().origin).solver CMDecomposition.CHOLESKY -> CholeskyDecomposition(a.toCM()).solver
} }
public fun CMLinearSpace.solve( public fun CMLinearSpace.solve(
a: Matrix<Float64>, a: Matrix<Float64>,
b: Matrix<Float64>, b: Matrix<Float64>,
decomposition: CMDecomposition = CMDecomposition.LUP, decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).solve(b.toCM().origin).wrap() ): CMMatrix = cmSolver(a, decomposition).solve(b.toCM()).asMatrix()
public fun CMLinearSpace.solve( public fun CMLinearSpace.solve(
a: Matrix<Float64>, a: Matrix<Float64>,
b: Point<Float64>, b: Point<Float64>,
decomposition: CMDecomposition = CMDecomposition.LUP, decomposition: CMDecomposition = CMDecomposition.LUP,
): CMVector = solver(a, decomposition).solve(b.toCM().origin).toPoint() ): CMVector = cmSolver(a, decomposition).solve(b.toCM()).asVector()
public fun CMLinearSpace.inverse( public fun CMLinearSpace.inverse(
a: Matrix<Float64>, a: Matrix<Float64>,
decomposition: CMDecomposition = CMDecomposition.LUP, decomposition: CMDecomposition = CMDecomposition.LUP,
): CMMatrix = solver(a, decomposition).inverse.wrap() ): CMMatrix = cmSolver(a, decomposition).inverse.asMatrix()
public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver<Float64> = object : LinearSolver<Float64> { public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver<Float64> =
object : LinearSolver<Float64> {
override fun solve(a: Matrix<Float64>, b: Matrix<Float64>): Matrix<Float64> = override fun solve(a: Matrix<Float64>, b: Matrix<Float64>): Matrix<Float64> =
solver(a, decomposition).solve(b.toCM().origin).wrap() cmSolver(a, decomposition).solve(b.toCM()).asMatrix()
override fun solve(a: Matrix<Float64>, b: Point<Float64>): Point<Float64> = override fun solve(a: Matrix<Float64>, b: Point<Float64>): Point<Float64> =
solver(a, decomposition).solve(b.toCM().origin).toPoint() cmSolver(a, decomposition).solve(b.toCM()).asVector()
override fun inverse(matrix: Matrix<Float64>): Matrix<Float64> = solver(matrix, decomposition).inverse.wrap() override fun inverse(matrix: Matrix<Float64>): Matrix<Float64> = cmSolver(matrix, decomposition).inverse.asMatrix()
} }
public fun CMLinearSpace.lupSolver(): LinearSolver<Float64> = solver((CMDecomposition.LUP)) public fun CMLinearSpace.lupSolver(): LinearSolver<Float64> = solver((CMDecomposition.LUP))

View File

@ -34,7 +34,7 @@ public object CMOptimizerEngine : OptimizationAttribute<() -> MultivariateOptimi
* Specify a Commons-maths optimization engine * Specify a Commons-maths optimization engine
*/ */
public fun AttributesBuilder<FunctionOptimization<Float64>>.cmEngine(optimizerBuilder: () -> MultivariateOptimizer) { public fun AttributesBuilder<FunctionOptimization<Float64>>.cmEngine(optimizerBuilder: () -> MultivariateOptimizer) {
set(CMOptimizerEngine, optimizerBuilder) CMOptimizerEngine(optimizerBuilder)
} }
public object CMOptimizerData : SetAttribute<SymbolIndexer.() -> OptimizationData> public object CMOptimizerData : SetAttribute<SymbolIndexer.() -> OptimizationData>

View File

@ -6,7 +6,7 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
/** /**
* A group of methods to solve for *X* in equation *X = A<sup>&minus;1</sup> &middot; B*, where *A* and *B* are * A group of methods to solve for $X$ in equation $X = A^{-1} \cdot B$, where $A$ and $B$ are
* matrices or vectors. * matrices or vectors.
* *
* @param T the type of items. * @param T the type of items.

View File

@ -8,6 +8,7 @@ package space.kscience.kmath.linear
import space.kscience.attributes.Attribute import space.kscience.attributes.Attribute
import space.kscience.attributes.Attributes import space.kscience.attributes.Attributes
import space.kscience.attributes.withAttribute import space.kscience.attributes.withAttribute
import space.kscience.attributes.withFlag
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
@ -47,7 +48,7 @@ public fun <T, A : Attribute<T>> Matrix<T>.withAttribute(
public fun <T, A : Attribute<Unit>> Matrix<T>.withAttribute( public fun <T, A : Attribute<Unit>> Matrix<T>.withAttribute(
attribute: A, attribute: A,
): MatrixWrapper<T> = if (this is MatrixWrapper) { ): MatrixWrapper<T> = if (this is MatrixWrapper) {
MatrixWrapper(origin, attributes.withAttribute(attribute)) MatrixWrapper(origin, attributes.withFlag(attribute))
} else { } else {
MatrixWrapper(this, Attributes(attribute, Unit)) MatrixWrapper(this, Attributes(attribute, Unit))
} }

View File

@ -15,7 +15,7 @@ import space.kscience.kmath.nd.StructureAttribute
* A marker interface for algebras that operate on matrices * A marker interface for algebras that operate on matrices
* @param T type of matrix element * @param T type of matrix element
*/ */
public interface MatrixScope<T> : AttributeScope<Matrix<T>>, WithType<T> public interface MatrixScope<T> : WithType<T>
/** /**
* A marker interface representing some properties of matrices or additional transformations of them. Features are used * A marker interface representing some properties of matrices or additional transformations of them. Features are used

View File

@ -117,12 +117,13 @@ internal class MutableBuffer1DWrapper<T>(val buffer: MutableBuffer<T>) : Mutable
/** /**
* Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch. * Represent a [StructureND] as [Structure1D]. Throw error in case of dimension mismatch.
*/ */
public fun <T> StructureND<T>.as1D(): Structure1D<T> = this as? Structure1D<T> ?: if (shape.size == 1) { public fun <T> StructureND<T>.as1D(): Structure1D<T> =
this as? Structure1D<T> ?: if (shape.size == 1) {
when (this) { when (this) {
is BufferND -> Buffer1DWrapper(this.buffer) is BufferND -> Buffer1DWrapper(this.buffer)
else -> Structure1DWrapper(this) else -> Structure1DWrapper(this)
} }
} else error("Can't create 1d-structure from ${shape.size}d-structure") } else error("Can't create 1d-structure from ${shape.size}d-structure")
public fun <T> MutableStructureND<T>.as1D(): MutableStructure1D<T> = public fun <T> MutableStructureND<T>.as1D(): MutableStructure1D<T> =
this as? MutableStructure1D<T> ?: if (shape.size == 1) { this as? MutableStructure1D<T> ?: if (shape.size == 1) {
@ -133,13 +134,3 @@ public fun <T> MutableStructureND<T>.as1D(): MutableStructure1D<T> =
* Represent this buffer as 1D structure * Represent this buffer as 1D structure
*/ */
public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this) public fun <T> Buffer<T>.asND(): Structure1D<T> = Buffer1DWrapper(this)
/**
* Expose inner buffer of this [Structure1D] if possible
*/
internal fun <T : Any> Structure1D<T>.asND(): Buffer<T> = when {
this is Buffer1DWrapper<T> -> buffer
this is Structure1DWrapper && structure is BufferND<T> -> structure.buffer
else -> this
}

View File

@ -229,6 +229,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace<Double, Float64Field, DMatri
} }
Determinant -> CommonOps_DDRM.det(origin) Determinant -> CommonOps_DDRM.det(origin)
SVD -> object : SingularValueDecomposition<Float64> { SVD -> object : SingularValueDecomposition<Float64> {
val ejmlSvd by lazy { val ejmlSvd by lazy {
DecompositionFactory_DDRM DecompositionFactory_DDRM

View File

@ -1,23 +0,0 @@
plugins {
id("space.kscience.gradle.jvm")
}
description = "Jafama integration module"
dependencies {
api(project(":kmath-core"))
api("net.jafama:jafama:2.3.2")
}
repositories {
mavenCentral()
}
readme {
maturity = space.kscience.gradle.Maturity.DEPRECATED
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
feature("jafama-double", "src/main/kotlin/space/kscience/kmath/jafama/") {
"Double ExtendedField implementations based on Jafama"
}
}

View File

@ -0,0 +1,22 @@
plugins {
id("space.kscience.gradle.mpp")
}
description = "Ojalgo bindings for kmath"
kscience {
jvm()
jvmMain {
api(projects.kmathCore)
// api(projects.kmathComplex)
// api(projects.kmathCoroutines)
// api(projects.kmathOptimization)
// api(projects.kmathStat)
// api(projects.kmathFunctions)
api(libs.ojalgo)
}
}
readme {
maturity = space.kscience.gradle.Maturity.PROTOTYPE
}

View File

@ -0,0 +1,35 @@
/*
* 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.ojalgo
import org.ojalgo.matrix.decomposition.*
import org.ojalgo.matrix.store.PhysicalStore
import org.ojalgo.matrix.store.R064Store
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.structures.Float64
public class Ojalgo<T : Comparable<T>, A : Ring<T>>(
public val elementAlgebra: A,
public val storeFactory: PhysicalStore.Factory<T, *>,
public val lu: LU.Factory<T>,
public val cholesky: Cholesky.Factory<T>,
public val qr: QR.Factory<T>,
public val svd: SingularValue.Factory<T>,
public val eigen: Eigenvalue.Factory<T>
) {
public companion object {
public val R064: Ojalgo<Float64, Float64Field> = Ojalgo(
elementAlgebra = Float64Field,
storeFactory = R064Store.FACTORY,
lu = LU.R064,
cholesky = Cholesky.R064,
qr = QR.R064,
svd = SingularValue.R064,
eigen = Eigenvalue.R064
)
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright 2018-2024 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.ojalgo
import org.ojalgo.matrix.store.MatrixStore
import org.ojalgo.matrix.store.PhysicalStore
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.linear.*
import space.kscience.kmath.nd.StructureAttribute
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.IntBuffer
import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.asList
@JvmInline
public value class OjalgoBuffer<T : Comparable<T>>(public val ojalgoMatrix: MatrixStore<T>) : Buffer<T> {
override val size: Int get() = ojalgoMatrix.size()
override fun get(index: Int): T = ojalgoMatrix.get(index.toLong())
override fun toString(): String = ojalgoMatrix.toString()
}
@JvmInline
public value class OjalgoMatrix<T : Comparable<T>>(public val ojalgoVector: MatrixStore<T>) : Matrix<T> {
override val rowNum: Int get() = ojalgoVector.rowDim
override val colNum: Int get() = ojalgoVector.colDim
override fun get(i: Int, j: Int): T = ojalgoVector.get(i.toLong(), j.toLong())
}
public class OjalgoLinearSpace<T : Comparable<T>, A : Ring<T>>(
public val ojalgo: Ojalgo<T, A>
) : LinearSpace<T, A> {
override val elementAlgebra: A
get() = ojalgo.elementAlgebra
public fun MatrixStore<T>.asMatrix(): OjalgoMatrix<T> = OjalgoMatrix(this)
public fun MatrixStore<T>.asVector(): OjalgoBuffer<T> = OjalgoBuffer(this)
/**
* If this matrix is [OjalgoMatrix] return it without conversion, otherwise create new [PhysicalStore]
*/
@OptIn(UnstableKMathAPI::class)
public fun Matrix<T>.toOjalgo(): MatrixStore<T> = when (val matrix = origin) {
is OjalgoMatrix<T> -> matrix.ojalgoVector
else -> ojalgo.storeFactory.make(rowNum.toLong(), colNum.toLong()).apply {
for (row in 0 until rowNum) {
for (column in 0 until colNum) {
set(row.toLong(), column.toLong(), get(row, column))
}
}
}
}
/**
* If this vector is [OjalgoBuffer] return it without conversion, otherwise create new [PhysicalStore]
*/
public fun Point<T>.toOjalgo(): MatrixStore<T> =
(this as? OjalgoBuffer<T>)?.ojalgoMatrix ?: ojalgo.storeFactory.column(asList())
override fun buildMatrix(
rows: Int,
columns: Int,
initializer: A.(Int, Int) -> T
): Matrix<T> {
val structure: MatrixStore<T> = ojalgo.storeFactory.make(rows.toLong(), columns.toLong()).apply {
for (row in 0 until rows) {
for (column in 0 until columns) {
set(row.toLong(), column.toLong(), elementAlgebra.initializer(row, column))
}
}
}
return OjalgoMatrix(structure)
}
override fun buildVector(size: Int, initializer: A.(Int) -> T): Point<T> {
val structure: MatrixStore<T> = ojalgo.storeFactory.column(List(size) { elementAlgebra.initializer(it) })
return OjalgoBuffer(structure)
}
@OptIn(UnstableKMathAPI::class)
override fun <V, A : StructureAttribute<V>> computeAttribute(
structure: Matrix<T>,
attribute: A
): V? {
val origin = structure.toOjalgo()
val raw: Any? = when (attribute) {
Determinant -> ojalgo.lu.make(origin).apply { decompose(origin) }.determinant
Inverted -> ojalgo.lu.make().apply { decompose(origin) }.inverse.asMatrix()
LUP -> object : LupDecomposition<T> {
val lup by lazy {
ojalgo.lu.make(origin).apply { decompose(origin) }
}
override val pivot: IntBuffer get() = lup.pivotOrder.asBuffer()
override val l: Matrix<T> get() = lup.l.asMatrix().withAttribute(LowerTriangular)
override val u: Matrix<T> get() = lup.u.asMatrix().withAttribute(UpperTriangular)
}
Cholesky -> object : CholeskyDecomposition<T> {
val cholesky by lazy {
ojalgo.cholesky.make(origin).apply { decompose(origin) }
}
override val l: Matrix<T> get() = cholesky.l.asMatrix()
}
QR -> object : QRDecomposition<T> {
val qr by lazy {
ojalgo.qr.make(origin).apply { decompose(origin) }
}
override val q: Matrix<T> get() = qr.q.asMatrix().withAttribute(OrthogonalAttribute)
override val r: Matrix<T> get() = qr.r.asMatrix().withAttribute(UpperTriangular)
}
SVD -> object : SingularValueDecomposition<T> {
val svd by lazy {
ojalgo.svd.make(origin).apply { decompose(origin) }
}
override val u: Matrix<T> get() = svd.u.asMatrix()
override val s: Matrix<T> get() = ojalgo.storeFactory.makeDiagonal(svd.singularValues).get().asMatrix()
override val v: Matrix<T> get() = svd.v.asMatrix()
override val singularValues: Point<T>
get() = ojalgo.storeFactory.asFactory1D().make(svd.singularValues).asList().asBuffer()
}
EIG -> object : EigenDecomposition<T> {
val eigen by lazy {
ojalgo.eigen.make(origin).apply { decompose(origin) }
}
override val v: Matrix<T> get() = eigen.v.asMatrix()
override val d: Matrix<T> get() = eigen.d.asMatrix()
}
else -> null
}
@Suppress("UNCHECKED_CAST")
return raw as V?
}
override fun Matrix<T>.times(value: T): OjalgoMatrix<T> = toOjalgo().multiply(value).asMatrix()
override fun Matrix<T>.dot(vector: Point<T>): OjalgoBuffer<T> = toOjalgo().multiply(vector.toOjalgo()).asVector()
override fun Matrix<T>.dot(other: Matrix<T>): OjalgoMatrix<T> = toOjalgo().multiply(other.toOjalgo()).asMatrix()
override fun Point<T>.times(value: T): OjalgoBuffer<T> = toOjalgo().multiply(value).asVector()
override fun Point<T>.minus(other: Point<T>): OjalgoBuffer<T> = toOjalgo().subtract(other.toOjalgo()).asVector()
override fun Matrix<T>.minus(other: Matrix<T>): OjalgoMatrix<T> = toOjalgo().subtract(other.toOjalgo()).asMatrix()
override fun Point<T>.plus(other: Point<T>): OjalgoBuffer<T> = toOjalgo().subtract(other.toOjalgo()).asVector()
override fun Matrix<T>.plus(other: Matrix<T>): OjalgoMatrix<T> = toOjalgo().add(other.toOjalgo()).asMatrix()
override fun Point<T>.unaryMinus(): OjalgoBuffer<T> = toOjalgo().negate().asVector()
override fun Matrix<T>.unaryMinus(): OjalgoMatrix<T> = toOjalgo().negate().asMatrix()
}
public val <T : Comparable<T>, A : Ring<T>> Ojalgo<T, A>.linearSpace: OjalgoLinearSpace<T, A>
get() = OjalgoLinearSpace(this)

View File

@ -0,0 +1,76 @@
/*
* 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.ojalgo
import org.junit.jupiter.api.Test
import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.linear.*
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.structures.Float64
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@UnstableKMathAPI
@OptIn(PerformancePitfall::class)
@Suppress("UNUSED_VARIABLE")
class OjalgoMatrixTest {
@Test
fun testTranspose() = with(Ojalgo.Companion.R064.linearSpace) {
val matrix = one(3, 3)
val transposed = matrix.transposed()
assertTrue { StructureND.Companion.contentEquals(matrix, transposed) }
}
@Test
fun testBuilder() = Ojalgo.Companion.R064.linearSpace {
val matrix = matrix(2, 3)(
1.0, 0.0, 0.0,
0.0, 1.0, 2.0
)
assertEquals(2.0, matrix[1, 2])
}
@Test
fun testMatrixExtension() = with(Ojalgo.Companion.R064.linearSpace) {
val transitionMatrix: Matrix<Float64> = VirtualMatrix(6, 6) { row, col ->
when {
col == 0 -> .50
row + 1 == col -> .50
row == 5 && col == 5 -> 1.0
else -> 0.0
}
}
infix fun Matrix<Float64>.pow(power: Int): Matrix<Float64> {
var res = this
repeat(power - 1) {
res = res dot this@pow
}
return res
}
val toTenthPower = transitionMatrix pow 10
}
@Test
fun test2DDot() = with(Ojalgo.Companion.R064.linearSpace) {
val firstMatrix = buildMatrix(2, 3) { i, j -> (i + j).toDouble() }
val secondMatrix = buildMatrix(3, 2) { i, j -> (i + j).toDouble() }
// val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() }
// val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() }
val result = firstMatrix dot secondMatrix
assertEquals(2, result.rowNum)
assertEquals(2, result.colNum)
assertEquals(8.0, result[0, 1])
assertEquals(8.0, result[1, 0])
assertEquals(14.0, result[1, 1])
}
}

View File

@ -12,7 +12,7 @@ import space.kscience.kmath.expressions.Symbol
public class OptimizationValue<V>(type: SafeType<V>) : PolymorphicAttribute<V>(type) public class OptimizationValue<V>(type: SafeType<V>) : PolymorphicAttribute<V>(type)
public inline fun <reified T> AttributesBuilder<FunctionOptimization<T>>.value(value: T) { public inline fun <reified T> AttributesBuilder<FunctionOptimization<T>>.value(value: T) {
set(OptimizationValue(safeTypeOf<T>()), value) put(OptimizationValue(safeTypeOf<T>()), value)
} }
public enum class OptimizationDirection { public enum class OptimizationDirection {

View File

@ -24,7 +24,7 @@ public val <T> OptimizationProblem<T>.startPoint: Map<Symbol, T>
get() = attributes[OptimizationStartPoint()] ?: error("Starting point not defined in $this") get() = attributes[OptimizationStartPoint()] ?: error("Starting point not defined in $this")
public fun <T> AttributesBuilder<OptimizationProblem<T>>.startAt(startingPoint: Map<Symbol, T>) { public fun <T> AttributesBuilder<OptimizationProblem<T>>.startAt(startingPoint: Map<Symbol, T>) {
set(OptimizationStartPoint(), startingPoint) put(OptimizationStartPoint(), startingPoint)
} }
@ -35,7 +35,7 @@ public class OptimizationCovariance<T> : OptimizationAttribute<NamedMatrix<T>>,
PolymorphicAttribute<NamedMatrix<T>>(safeTypeOf()) PolymorphicAttribute<NamedMatrix<T>>(safeTypeOf())
public fun <T> AttributesBuilder<OptimizationProblem<T>>.covariance(covariance: NamedMatrix<T>) { public fun <T> AttributesBuilder<OptimizationProblem<T>>.covariance(covariance: NamedMatrix<T>) {
set(OptimizationCovariance(), covariance) put(OptimizationCovariance(), covariance)
} }
@ -43,7 +43,7 @@ public class OptimizationResult<T>() : OptimizationAttribute<Map<Symbol, T>>,
PolymorphicAttribute<Map<Symbol, T>>(safeTypeOf()) PolymorphicAttribute<Map<Symbol, T>>(safeTypeOf())
public fun <T> AttributesBuilder<OptimizationProblem<T>>.result(result: Map<Symbol, T>) { public fun <T> AttributesBuilder<OptimizationProblem<T>>.result(result: Map<Symbol, T>) {
set(OptimizationResult(), result) put(OptimizationResult(), result)
} }
public val <T> OptimizationProblem<T>.resultOrNull: Map<Symbol, T>? get() = attributes[OptimizationResult()] public val <T> OptimizationProblem<T>.resultOrNull: Map<Symbol, T>? get() = attributes[OptimizationResult()]

View File

@ -125,9 +125,9 @@ public suspend fun XYColumnarData<Double, Double, Double>.fitWith(
this, this,
modelExpression, modelExpression,
attributes.modified<XYFit> { attributes.modified<XYFit> {
set(OptimizationStartPoint(), startingPoint) put(OptimizationStartPoint(), startingPoint)
if (!hasAny<OptimizationLog>()) { if (!attributes.hasAny<OptimizationLog>()) {
set(OptimizationLog, Loggable.console) put(OptimizationLog, Loggable.console)
} }
}, },
pointToCurveDistance, pointToCurveDistance,

View File

@ -38,7 +38,6 @@ dependencyResolutionManagement {
include( include(
":test-utils", ":test-utils",
":attributes-kt",
":kmath-memory", ":kmath-memory",
":kmath-complex", ":kmath-complex",
":kmath-core", ":kmath-core",
@ -61,6 +60,7 @@ include(
":kmath-tensors", ":kmath-tensors",
":kmath-jupyter", ":kmath-jupyter",
":kmath-symja", ":kmath-symja",
":kmath-ojalgo",
":examples", ":examples",
":benchmarks", ":benchmarks",
) )