From 53db4489f8314990ba1a2fbb2831da36e8512493 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 19 Jan 2021 01:09:44 +0700 Subject: [PATCH 1/6] Implement decomposition features by CMMatrix --- .../kscience/kmath/commons/linear/CMMatrix.kt | 38 +++++++++++++++++-- .../kscience/kmath/linear/MatrixFeatures.kt | 2 + .../kotlin/kscience/kmath/ejml/EjmlMatrix.kt | 8 +++- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index 49888f8d6..a40657cfb 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -3,15 +3,45 @@ package kscience.kmath.commons.linear import kscience.kmath.linear.* import kscience.kmath.structures.Matrix import kscience.kmath.structures.NDStructure +import kscience.kmath.structures.RealBuffer import org.apache.commons.math3.linear.* -public class CMMatrix(public val origin: RealMatrix, features: Set? = null) : FeaturedMatrix { +public class CMMatrix(public val origin: RealMatrix, features: Set = emptySet()) : + FeaturedMatrix { public override val rowNum: Int get() = origin.rowDimension public override val colNum: Int get() = origin.columnDimension - public override val features: Set = features ?: sequence { - if (origin is DiagonalMatrix) yield(DiagonalFeature) - }.toHashSet() + public override val features: Set = features union hashSetOf( + *if (origin is DiagonalMatrix) arrayOf(DiagonalFeature) else emptyArray(), + object : DeterminantFeature, LupDecompositionFeature { + private val lup by lazy { LUDecomposition(origin) } + override val determinant: Double by lazy { lup.determinant } + override val l: FeaturedMatrix by lazy { CMMatrix(lup.l, setOf(LFeature)) } + override val u: FeaturedMatrix by lazy { CMMatrix(lup.u, setOf(UFeature)) } + override val p: FeaturedMatrix by lazy { CMMatrix(lup.p) } + }, + + object : CholeskyDecompositionFeature { + override val l: FeaturedMatrix by lazy { + val cholesky = CholeskyDecomposition(origin) + CMMatrix(cholesky.l, setOf(LFeature)) + } + }, + + object : QRDecompositionFeature { + private val qr by lazy { QRDecomposition(origin) } + override val q: FeaturedMatrix by lazy { CMMatrix(qr.q, setOf(OrthogonalFeature)) } + override val r: FeaturedMatrix by lazy { CMMatrix(qr.r, setOf(UFeature)) } + }, + + object : SingularValueDecompositionFeature { + private val sv by lazy { SingularValueDecomposition(origin) } + override val u: FeaturedMatrix by lazy { CMMatrix(sv.u) } + override val s: FeaturedMatrix by lazy { CMMatrix(sv.s) } + override val v: FeaturedMatrix by lazy { CMMatrix(sv.v) } + override val singularValues: Point by lazy { RealBuffer(sv.singularValues) } + }, + ) public override fun suggestFeature(vararg features: MatrixFeature): CMMatrix = CMMatrix(origin, this.features + features) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt index 767b58eba..81e734949 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt @@ -35,6 +35,8 @@ public interface InverseMatrixFeature : MatrixFeature { /** * Matrices with this feature can compute their determinant. + * + * @param T the type of matrices' items. */ public interface DeterminantFeature : MatrixFeature { /** diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt index 5b7d0a01b..0fb361040 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt @@ -48,8 +48,12 @@ public class EjmlMatrix(public val origin: SimpleMatrix, features: Set by lazy { EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) } - override val r: FeaturedMatrix by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) } + override val q: FeaturedMatrix by lazy { + EjmlMatrix(SimpleMatrix(qr.getQ(null, false)), setOf(OrthogonalFeature)) + } + override val r: FeaturedMatrix by lazy { + EjmlMatrix(SimpleMatrix(qr.getR(null, false)), setOf(UFeature)) + } }, object : CholeskyDecompositionFeature { From 348f114bb6231abe776b6c9f201159d8697f4acf Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 19 Jan 2021 01:10:16 +0700 Subject: [PATCH 2/6] Minor: add newline --- .../src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index a40657cfb..845fe32b5 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -13,6 +13,7 @@ public class CMMatrix(public val origin: RealMatrix, features: Set = features union hashSetOf( *if (origin is DiagonalMatrix) arrayOf(DiagonalFeature) else emptyArray(), + object : DeterminantFeature, LupDecompositionFeature { private val lup by lazy { LUDecomposition(origin) } override val determinant: Double by lazy { lup.determinant } From 57b1157650de6ada389c371d0730e66f30dcc3c7 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 20 Jan 2021 00:28:39 +0700 Subject: [PATCH 3/6] Reformat, bring back the features of CMMatrix with the new API, add missing features in QRDecomposition in EjmlMatrix --- .../kscience/kmath/commons/linear/CMMatrix.kt | 40 ++++++++++++++++--- .../kscience/kmath/linear/MatrixFeatures.kt | 4 +- .../kotlin/kscience/kmath/misc/annotations.kt | 2 +- .../kotlin/kscience/kmath/ejml/EjmlMatrix.kt | 26 +++++++----- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt index 48b6e0ef1..c9b3f7117 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/linear/CMMatrix.kt @@ -1,11 +1,9 @@ package kscience.kmath.commons.linear -import kscience.kmath.linear.DiagonalFeature -import kscience.kmath.linear.MatrixContext -import kscience.kmath.linear.MatrixWrapper -import kscience.kmath.linear.Point +import kscience.kmath.linear.* import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.structures.Matrix +import kscience.kmath.structures.RealBuffer import org.apache.commons.math3.linear.* import kotlin.reflect.KClass import kotlin.reflect.cast @@ -17,8 +15,40 @@ public inline class CMMatrix(public val origin: RealMatrix) : Matrix { @UnstableKMathAPI override fun getFeature(type: KClass): T? = when (type) { DiagonalFeature::class -> if (origin is DiagonalMatrix) DiagonalFeature else null + + DeterminantFeature::class, LupDecompositionFeature::class -> object : + DeterminantFeature, + LupDecompositionFeature { + private val lup by lazy { LUDecomposition(origin) } + override val determinant: Double by lazy { lup.determinant } + override val l: Matrix by lazy { CMMatrix(lup.l) + LFeature } + override val u: Matrix by lazy { CMMatrix(lup.u) + UFeature } + override val p: Matrix by lazy { CMMatrix(lup.p) } + } + + CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { + override val l: Matrix by lazy { + val cholesky = CholeskyDecomposition(origin) + CMMatrix(cholesky.l) + LFeature + } + } + + QRDecompositionFeature::class -> object : QRDecompositionFeature { + private val qr by lazy { QRDecomposition(origin) } + override val q: Matrix by lazy { CMMatrix(qr.q) + OrthogonalFeature } + override val r: Matrix by lazy { CMMatrix(qr.r) + UFeature } + } + + SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { + private val sv by lazy { SingularValueDecomposition(origin) } + override val u: Matrix by lazy { CMMatrix(sv.u) } + override val s: Matrix by lazy { CMMatrix(sv.s) } + override val v: Matrix by lazy { CMMatrix(sv.v) } + override val singularValues: Point by lazy { RealBuffer(sv.singularValues) } + } + else -> null - }?.let { type.cast(it) } + }?.let(type::cast) public override operator fun get(i: Int, j: Int): Double = origin.getEntry(i, j) } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt index 8a0be4338..8d5c61ab9 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/MatrixFeatures.kt @@ -11,8 +11,8 @@ public interface MatrixFeature /** * Matrices with this feature are considered to have only diagonal non-null elements. */ -public interface DiagonalFeature : MatrixFeature{ - public companion object: DiagonalFeature +public interface DiagonalFeature : MatrixFeature { + public companion object : DiagonalFeature } /** diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt index d70ac7b39..06248a166 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/misc/annotations.kt @@ -1,4 +1,4 @@ package kscience.kmath.misc @RequiresOptIn("This API is unstable and could change in future", RequiresOptIn.Level.WARNING) -public annotation class UnstableKMathAPI \ No newline at end of file +public annotation class UnstableKMathAPI diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt index 1c2ded447..d81082dc4 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt @@ -15,21 +15,20 @@ import kotlin.reflect.cast * @property origin the underlying [SimpleMatrix]. * @author Iaroslav Postovalov */ -public inline class EjmlMatrix( - public val origin: SimpleMatrix, -) : Matrix { +public inline class EjmlMatrix(public val origin: SimpleMatrix) : Matrix { public override val rowNum: Int get() = origin.numRows() - public override val colNum: Int get() = origin.numCols() @UnstableKMathAPI - override fun getFeature(type: KClass): T? = when (type) { + public override fun getFeature(type: KClass): T? = when (type) { InverseMatrixFeature::class -> object : InverseMatrixFeature { override val inverse: Matrix by lazy { EjmlMatrix(origin.invert()) } } + DeterminantFeature::class -> object : DeterminantFeature { override val determinant: Double by lazy(origin::determinant) } + SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { private val svd by lazy { DecompositionFactory_DDRM.svd(origin.numRows(), origin.numCols(), true, true, false) @@ -41,15 +40,20 @@ public inline class EjmlMatrix( override val v: Matrix by lazy { EjmlMatrix(SimpleMatrix(svd.getV(null, false))) } override val singularValues: Point by lazy { RealBuffer(svd.singularValues) } } + QRDecompositionFeature::class -> object : QRDecompositionFeature { private val qr by lazy { DecompositionFactory_DDRM.qr().apply { decompose(origin.ddrm.copy()) } } - override val q: Matrix by lazy { EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) } - override val r: Matrix by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) } + override val q: Matrix by lazy { + EjmlMatrix(SimpleMatrix(qr.getQ(null, false))) + OrthogonalFeature + } + + override val r: Matrix by lazy { EjmlMatrix(SimpleMatrix(qr.getR(null, false))) + UFeature } } - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { + + CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { override val l: Matrix by lazy { val cholesky = DecompositionFactory_DDRM.chol(rowNum, true).apply { decompose(origin.ddrm.copy()) } @@ -57,7 +61,8 @@ public inline class EjmlMatrix( EjmlMatrix(SimpleMatrix(cholesky.getT(null))) + LFeature } } - LupDecompositionFeature::class -> object : LupDecompositionFeature { + + LupDecompositionFeature::class -> object : LupDecompositionFeature { private val lup by lazy { DecompositionFactory_DDRM.lu(origin.numRows(), origin.numCols()).apply { decompose(origin.ddrm.copy()) } } @@ -72,8 +77,9 @@ public inline class EjmlMatrix( override val p: Matrix by lazy { EjmlMatrix(SimpleMatrix(lup.getRowPivot(null))) } } + else -> null - }?.let{type.cast(it)} + }?.let(type::cast) public override operator fun get(i: Int, j: Int): Double = origin[i, j] } From 72832bae10d65d92931de1929be53c63d87c73df Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 20 Jan 2021 16:51:36 +0700 Subject: [PATCH 4/6] Minor: reformat --- kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt index a71f0501d..0586c065c 100644 --- a/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt +++ b/kmath-ejml/src/main/kotlin/kscience/kmath/ejml/EjmlMatrix.kt @@ -16,11 +16,8 @@ import kotlin.reflect.cast * @property origin the underlying [SimpleMatrix]. * @author Iaroslav Postovalov */ -public class EjmlMatrix( - public val origin: SimpleMatrix, -) : Matrix { +public class EjmlMatrix(public val origin: SimpleMatrix) : Matrix { public override val rowNum: Int get() = origin.numRows() - public override val colNum: Int get() = origin.numCols() @UnstableKMathAPI From b20081f161d3ba8814c1ad7a7a55fb33788ea9bb Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 24 Jan 2021 22:12:43 +0700 Subject: [PATCH 5/6] Fix capitalization of LUP related references (LUP -> Lup) --- CHANGELOG.md | 2 +- .../benchmarks/LinearAlgebraBenchmark.kt | 4 ++-- .../kscience/kmath/linear/LupDecomposition.kt | 24 +++++++++---------- .../kscience/kmath/linear/RealLUSolverTest.kt | 4 ++-- .../kotlin/kscience/kmath/real/RealMatrix.kt | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa70e6116..3ee260775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ - Use `Point` instead of specialized type in `kmath-for-real` - Optimized dot product for buffer matrices moved to `kmath-for-real` - EjmlMatrix context is an object -- Matrix LUP `inverse` renamed to `inverseWithLUP` +- Matrix LUP `inverse` renamed to `inverseWithLup` - `NumericAlgebra` moved outside of regular algebra chain (`Ring` no longer implements it). - Features moved to NDStructure and became transparent. diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt index 5ff43ef80..283210174 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/benchmarks/LinearAlgebraBenchmark.kt @@ -26,8 +26,8 @@ class LinearAlgebraBenchmark { } @Benchmark - fun kmathLUPInversion() { - MatrixContext.real.inverseWithLUP(matrix) + fun kmathLupInversion() { + MatrixContext.real.inverseWithLup(matrix) } @Benchmark diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt index 5cf7c8f70..645df429c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/linear/LupDecomposition.kt @@ -151,7 +151,7 @@ public inline fun , F : Field> GenericMatrixContext public fun MatrixContext>.lup(matrix: Matrix): LupDecomposition = lup(Buffer.Companion::real, RealField, matrix) { it < 1e-11 } -public fun LupDecomposition.solveWithLUP( +public fun LupDecomposition.solveWithLup( factory: MutableBufferFactory, matrix: Matrix, ): Matrix { @@ -199,14 +199,14 @@ public fun LupDecomposition.solveWithLUP( } } -public inline fun LupDecomposition.solveWithLUP(matrix: Matrix): Matrix = - solveWithLUP(MutableBuffer.Companion::auto, matrix) +public inline fun LupDecomposition.solveWithLup(matrix: Matrix): Matrix = + solveWithLup(MutableBuffer.Companion::auto, matrix) /** - * Solve a linear equation **a*x = b** using LUP decomposition + * Solves a system of linear equations *ax = b** using LUP decomposition. */ @OptIn(UnstableKMathAPI::class) -public inline fun , F : Field> GenericMatrixContext>.solveWithLUP( +public inline fun , F : Field> GenericMatrixContext>.solveWithLup( a: Matrix, b: Matrix, noinline bufferFactory: MutableBufferFactory = MutableBuffer.Companion::auto, @@ -214,26 +214,26 @@ public inline fun , F : Field> GenericMatrixContext ): Matrix { // Use existing decomposition if it is provided by matrix val decomposition = a.getFeature() ?: lup(bufferFactory, elementContext, a, checkSingular) - return decomposition.solveWithLUP(bufferFactory, b) + return decomposition.solveWithLup(bufferFactory, b) } -public inline fun , F : Field> GenericMatrixContext>.inverseWithLUP( +public inline fun , F : Field> GenericMatrixContext>.inverseWithLup( matrix: Matrix, noinline bufferFactory: MutableBufferFactory = MutableBuffer.Companion::auto, noinline checkSingular: (T) -> Boolean, -): Matrix = solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular) +): Matrix = solveWithLup(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular) @OptIn(UnstableKMathAPI::class) -public fun RealMatrixContext.solveWithLUP(a: Matrix, b: Matrix): Matrix { +public fun RealMatrixContext.solveWithLup(a: Matrix, b: Matrix): Matrix { // Use existing decomposition if it is provided by matrix val bufferFactory: MutableBufferFactory = MutableBuffer.Companion::real val decomposition: LupDecomposition = a.getFeature() ?: lup(bufferFactory, RealField, a) { it < 1e-11 } - return decomposition.solveWithLUP(bufferFactory, b) + return decomposition.solveWithLup(bufferFactory, b) } /** * Inverses a square matrix using LUP decomposition. Non square matrix will throw a error. */ -public fun RealMatrixContext.inverseWithLUP(matrix: Matrix): Matrix = - solveWithLUP(matrix, one(matrix.rowNum, matrix.colNum)) \ No newline at end of file +public fun RealMatrixContext.inverseWithLup(matrix: Matrix): Matrix = + solveWithLup(matrix, one(matrix.rowNum, matrix.colNum)) \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt index 28dfe46ec..d2822a221 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/linear/RealLUSolverTest.kt @@ -9,7 +9,7 @@ class RealLUSolverTest { @Test fun testInvertOne() { val matrix = MatrixContext.real.one(2, 2) - val inverted = MatrixContext.real.inverseWithLUP(matrix) + val inverted = MatrixContext.real.inverseWithLup(matrix) assertEquals(matrix, inverted) } @@ -37,7 +37,7 @@ class RealLUSolverTest { 1.0, 3.0 ) - val inverted = MatrixContext.real.inverseWithLUP(matrix) + val inverted = MatrixContext.real.inverseWithLup(matrix) val expected = Matrix.square( 0.375, -0.125, diff --git a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt index 274030aff..69e211624 100644 --- a/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/kscience/kmath/real/RealMatrix.kt @@ -2,7 +2,7 @@ package kscience.kmath.real import kscience.kmath.linear.MatrixContext import kscience.kmath.linear.VirtualMatrix -import kscience.kmath.linear.inverseWithLUP +import kscience.kmath.linear.inverseWithLup import kscience.kmath.linear.real import kscience.kmath.misc.UnstableKMathAPI import kscience.kmath.structures.Buffer @@ -152,7 +152,7 @@ public inline fun RealMatrix.map(transform: (Double) -> Double): RealMatrix = /** * Inverse a square real matrix using LUP decomposition */ -public fun RealMatrix.inverseWithLUP(): RealMatrix = MatrixContext.real.inverseWithLUP(this) +public fun RealMatrix.inverseWithLup(): RealMatrix = MatrixContext.real.inverseWithLup(this) //extended operations From e0b8a0bfe595bd16601e0d66d2acf779ff51d52d Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 24 Jan 2021 22:30:52 +0700 Subject: [PATCH 6/6] Update the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ee260775..fdf01d4de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Matrix LUP `inverse` renamed to `inverseWithLup` - `NumericAlgebra` moved outside of regular algebra chain (`Ring` no longer implements it). - Features moved to NDStructure and became transparent. +- Capitalization of LUP in many names changed to Lup. ### Deprecated