From 3c2b1920a535ea4706a101ae3f89fd847d995cc8 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Thu, 20 Jul 2023 12:19:59 +0200 Subject: [PATCH] wip --- .../lcaplugin/core/assessment/Assessment.kt | 4 +- .../kleis/lcaplugin/core/math/DualMatrix.kt | 68 +--- .../kleis/lcaplugin/core/math/DualNumber.kt | 18 +- .../ch/kleis/lcaplugin/core/math/MathUtils.kt | 69 ++++ .../lcaplugin/core/math/ejml/EJMLMatrix.kt | 2 +- .../lcaplugin/core/math/DualMatrixTest.kt | 92 +++++ .../lcaplugin/core/math/MathUtilsKtTest.kt | 128 +++++++ .../lcaplugin/core/math/MatrixFixture.kt | 18 +- .../core/math/ejml/EJMLMatrixTest.kt | 323 ++++++++++++++++++ .../core/matrix/ImpactFactorMatrixTest.kt | 13 +- 10 files changed, 644 insertions(+), 91 deletions(-) create mode 100644 src/main/kotlin/ch/kleis/lcaplugin/core/math/MathUtils.kt create mode 100644 src/test/kotlin/ch/kleis/lcaplugin/core/math/DualMatrixTest.kt create mode 100644 src/test/kotlin/ch/kleis/lcaplugin/core/math/MathUtilsKtTest.kt create mode 100644 src/test/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrixTest.kt diff --git a/src/main/kotlin/ch/kleis/lcaplugin/core/assessment/Assessment.kt b/src/main/kotlin/ch/kleis/lcaplugin/core/assessment/Assessment.kt index f3397657d..e1b9690e2 100644 --- a/src/main/kotlin/ch/kleis/lcaplugin/core/assessment/Assessment.kt +++ b/src/main/kotlin/ch/kleis/lcaplugin/core/assessment/Assessment.kt @@ -67,10 +67,10 @@ class Assessment( } fun inventory(): Inventory { - val impactFactorMatrix = this.observableMatrix.matrix.matDiv(this.controllableMatrix.matrix.negate()) + val impactFactorMatrix = this.controllableMatrix.matrix.negate().matDiv(this.observableMatrix.matrix) ?.let { ImpactFactorMatrix(observablePorts, controllablePorts, it) } ?: throw EvaluatorException("The system cannot be solved") - val supplyMatrix = this.observableMatrix.matrix.transpose().matDiv(demandMatrix.matrix.transpose()) + val supplyMatrix = demandMatrix.matrix.transpose().matDiv(this.observableMatrix.matrix.transpose()) ?.transpose() ?.let { SupplyMatrix(observablePorts, it) } ?: throw EvaluatorException("The system cannot be solved") diff --git a/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualMatrix.kt b/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualMatrix.kt index 3e1a65e9d..a25b23063 100644 --- a/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualMatrix.kt +++ b/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualMatrix.kt @@ -1,74 +1,19 @@ package ch.kleis.lcaplugin.core.math -import ch.kleis.lcaplugin.core.math.ejml.EJMLMatrix -import ch.kleis.lcaplugin.core.math.ejml.EJMLMatrixFactory -import org.ejml.data.DMatrixSparseCSC import org.jetbrains.kotlinx.multik.api.mk import org.jetbrains.kotlinx.multik.api.zeros import org.jetbrains.kotlinx.multik.ndarray.data.* -import org.jetbrains.kotlinx.multik.ndarray.operations.forEachMultiIndexed import org.jetbrains.kotlinx.multik.ndarray.operations.minus import org.jetbrains.kotlinx.multik.ndarray.operations.plus import org.jetbrains.kotlinx.multik.ndarray.operations.unaryMinus -/* - TODO: TEST ME !!! - */ -private fun d2Ejml(m: D2Array): EJMLMatrix { - val (rows, cols) = m.shape - val r = EJMLMatrixFactory.INSTANCE.zero(rows, cols) - m.forEachMultiIndexed { index, d -> - val (i, j) = index - r.set(i, j, d) - } - return r -} - -private fun d3Ejml(m: D3Array): EJMLMatrix { - val (rows, cols, nps) = m.shape - val nParams = if (nps == 0) 1 else nps - val r = EJMLMatrixFactory.INSTANCE.zero(rows, cols * nParams) - m.forEachMultiIndexed { index, d -> - val (i, j, k) = index - r.set(i, j * nParams + k, d) - } - return r -} - -private fun ejmlD2(m: EJMLMatrix): D2Array { - val (rows, cols) = Pair(m.rowDim(), m.colDim()) - val r = mk.zeros(rows, cols) - val iterator = m.matrix.getMatrix().createCoordinateIterator() - while (iterator.hasNext()) { - val v = iterator.next() - r[v.row, v.col] = v.value - } - return r -} - -private fun ejmlD3(m: EJMLMatrix, nParams: Int): D3Array { - val (rows, cols) = Pair(m.rowDim(), m.colDim()) - val r = mk.zeros(rows, cols, nParams) - if (nParams == 0) return r - val iterator = m.matrix.getMatrix().createCoordinateIterator() - while (iterator.hasNext()) { - val v = iterator.next() - val i = v.row - val j = v.col / nParams - val k = v.col % nParams - r[i, j, k] = v.value - } - return r -} - -open class DualMatrix( +data class DualMatrix( private val zeroth: D2Array, private val first: D3Array, ) { private val nParams = first.shape[2] - companion object { fun zeros(rows: Int, cols: Int, nParams: Int): DualMatrix { return DualMatrix( @@ -78,10 +23,10 @@ open class DualMatrix( } } - open fun value(row: Int, col: Int): DualNumber { + fun value(row: Int, col: Int): DualNumber { return DualNumber( zeroth[row, col], - first[row, col].asDNArray().asD1Array(), + first[row, col].flatten() as D1Array, ) } @@ -114,6 +59,11 @@ open class DualMatrix( ) } + /* + X = R \ L + L @ X = R + */ + fun matDiv(other: DualMatrix): DualMatrix? { val leftEjml0 = d2Ejml(this.zeroth) val rightEjml0 = d2Ejml(other.zeroth) @@ -157,7 +107,7 @@ open class DualMatrix( fun set(row: Int, col: Int, value: DualNumber) { zeroth[row, col] = value.zeroth - first[row, col] = value.first + first[row, col] = if (value.first.isEmpty()) mk.zeros(nParams) else value.first } fun add(row: Int, col: Int, value: DualNumber) { diff --git a/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualNumber.kt b/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualNumber.kt index 7b6ade79c..17b363104 100644 --- a/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualNumber.kt +++ b/src/main/kotlin/ch/kleis/lcaplugin/core/math/DualNumber.kt @@ -1,6 +1,5 @@ package ch.kleis.lcaplugin.core.math -import ch.kleis.lcaplugin.core.lang.evaluator.EvaluatorException import org.jetbrains.kotlinx.multik.api.d1array import org.jetbrains.kotlinx.multik.api.mk import org.jetbrains.kotlinx.multik.api.zeros @@ -17,15 +16,6 @@ data class DualNumber( return "$zeroth" } - private fun broadcast(l: D1Array, r: D1Array): Pair, D1Array> { - return when { - l.isEmpty() -> mk.zeros(r.size) to r - r.isEmpty() -> l to mk.zeros(l.size) - l.size != r.size -> throw EvaluatorException("d1arrays cannot be broadcast") - else -> l to r - } - } - companion object { fun constant(c: Double): DualNumber { return constant(c, 0) @@ -64,7 +54,7 @@ data class DualNumber( } operator fun plus(other: DualNumber): DualNumber { - val (thisFirst, otherFirst) = broadcast(this.first, other.first) + val (thisFirst, otherFirst) = d1MatchDimensions(this.first, other.first) return DualNumber( this.zeroth + other.zeroth, thisFirst + otherFirst, @@ -72,7 +62,7 @@ data class DualNumber( } operator fun minus(other: DualNumber): DualNumber { - val (thisFirst, otherFirst) = broadcast(this.first, other.first) + val (thisFirst, otherFirst) = d1MatchDimensions(this.first, other.first) return DualNumber( this.zeroth - other.zeroth, thisFirst - otherFirst, @@ -80,7 +70,7 @@ data class DualNumber( } operator fun times(other: DualNumber): DualNumber { - val (thisFirst, otherFirst) = broadcast(this.first, other.first) + val (thisFirst, otherFirst) = d1MatchDimensions(this.first, other.first) return DualNumber( this.zeroth * other.zeroth, thisFirst * other.zeroth + otherFirst * zeroth, @@ -88,7 +78,7 @@ data class DualNumber( } operator fun div(other: DualNumber): DualNumber { - val (thisFirst, otherFirst) = broadcast(this.first, other.first) + val (thisFirst, otherFirst) = d1MatchDimensions(this.first, other.first) return DualNumber( this.zeroth / other.zeroth, thisFirst / other.zeroth - otherFirst * this.zeroth / other.zeroth.pow(2) diff --git a/src/main/kotlin/ch/kleis/lcaplugin/core/math/MathUtils.kt b/src/main/kotlin/ch/kleis/lcaplugin/core/math/MathUtils.kt new file mode 100644 index 000000000..bcc2c0824 --- /dev/null +++ b/src/main/kotlin/ch/kleis/lcaplugin/core/math/MathUtils.kt @@ -0,0 +1,69 @@ +package ch.kleis.lcaplugin.core.math + +import ch.kleis.lcaplugin.core.lang.evaluator.EvaluatorException +import ch.kleis.lcaplugin.core.math.ejml.EJMLMatrix +import ch.kleis.lcaplugin.core.math.ejml.EJMLMatrixFactory +import org.ejml.data.DMatrixSparseCSC +import org.jetbrains.kotlinx.multik.api.mk +import org.jetbrains.kotlinx.multik.api.zeros +import org.jetbrains.kotlinx.multik.ndarray.data.D1Array +import org.jetbrains.kotlinx.multik.ndarray.data.D2Array +import org.jetbrains.kotlinx.multik.ndarray.data.D3Array +import org.jetbrains.kotlinx.multik.ndarray.data.set +import org.jetbrains.kotlinx.multik.ndarray.operations.forEachMultiIndexed + +fun d1MatchDimensions(l: D1Array, r: D1Array): Pair, D1Array> { + return when { + l.isEmpty() -> mk.zeros(r.size) to r + r.isEmpty() -> l to mk.zeros(l.size) + l.size != r.size -> throw EvaluatorException("d1arrays cannot be broadcast") + else -> l to r + } +} + +fun d2Ejml(m: D2Array): EJMLMatrix { + val (rows, cols) = m.shape + val r = EJMLMatrixFactory.INSTANCE.zero(rows, cols) + m.forEachMultiIndexed { index, d -> + val (i, j) = index + r.set(i, j, d) + } + return r +} + +fun d3Ejml(m: D3Array): EJMLMatrix { + val (rows, cols, nps) = m.shape + val nParams = if (nps == 0) 1 else nps + val r = EJMLMatrixFactory.INSTANCE.zero(rows, cols * nParams) + m.forEachMultiIndexed { index, d -> + val (i, j, k) = index + r.set(i, j * nParams + k, d) + } + return r +} + +fun ejmlD2(m: EJMLMatrix): D2Array { + val (rows, cols) = Pair(m.rowDim(), m.colDim()) + val r = mk.zeros(rows, cols) + val iterator = m.matrix.getMatrix().createCoordinateIterator() + while (iterator.hasNext()) { + val v = iterator.next() + r[v.row, v.col] = v.value + } + return r +} + +fun ejmlD3(m: EJMLMatrix, nParams: Int): D3Array { + val (rows, cols) = Pair(m.rowDim(), m.colDim()) + val r = mk.zeros(rows, cols, nParams) + if (nParams == 0) return r + val iterator = m.matrix.getMatrix().createCoordinateIterator() + while (iterator.hasNext()) { + val v = iterator.next() + val i = v.row + val j = v.col / nParams + val k = v.col % nParams + r[i, j, k] = v.value + } + return r +} diff --git a/src/main/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrix.kt b/src/main/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrix.kt index 6d93a2e8c..bb1072388 100644 --- a/src/main/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrix.kt +++ b/src/main/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrix.kt @@ -20,7 +20,7 @@ class EJMLMatrix(internal val matrix: SimpleMatrix) { fun matDiv(other: EJMLMatrix): EJMLMatrix? { val solver = EJMLSolver.INSTANCE - return solver.solve(this, other) + return solver.solve(other, this) } fun matMul(other: EJMLMatrix): EJMLMatrix { diff --git a/src/test/kotlin/ch/kleis/lcaplugin/core/math/DualMatrixTest.kt b/src/test/kotlin/ch/kleis/lcaplugin/core/math/DualMatrixTest.kt new file mode 100644 index 000000000..d14d81248 --- /dev/null +++ b/src/test/kotlin/ch/kleis/lcaplugin/core/math/DualMatrixTest.kt @@ -0,0 +1,92 @@ +package ch.kleis.lcaplugin.core.math + +import ch.kleis.lcaplugin.core.math.MatrixFixture.Companion.makeDualMatrix +import org.junit.Test +import kotlin.test.assertEquals + + +class DualMatrixTest { + private val dx = DualNumber.basis(2, 0) + private val dy = DualNumber.basis(2, 1) + private fun c(d: Double): DualNumber { + return DualNumber.constant(d) + } + + @Test + fun value() { + // given + val m = makeDualMatrix(2, 3, 2, + arrayOf( + c(1.0), c(2.0) + dx, c(3.0), + c(4.0), c(5.0), c(6.0), + ) + ) + + // when + val actual = m.value(0, 1, ) + + // then + val expected = c(2.0) + dx + assertEquals(expected, actual) + } + + @Test + fun set() { + // given + val m = makeDualMatrix(2, 3, 2, + arrayOf( + c(1.0), c(2.0), c(3.0), + c(4.0), c(5.0), c(6.0), + ) + ) + + // when + m.set(0, 1, c(3.0) + dx) + + // then + val expected = makeDualMatrix(2, 3, 2, + arrayOf( + c(1.0), c(3.0) + dx, c(3.0), + c(4.0), c(5.0), c(6.0), + ) + ) + assertEquals(expected, m) + } + + + @Test + fun minus() { + } + + @Test + fun plus() { + } + + @Test + fun matMul() { + } + + @Test + fun matDiv() { + } + + @Test + fun negate() { + } + + @Test + fun transpose() { + } + + @Test + fun rowDim() { + } + + @Test + fun colDim() { + } + + @Test + fun add() { + } +} diff --git a/src/test/kotlin/ch/kleis/lcaplugin/core/math/MathUtilsKtTest.kt b/src/test/kotlin/ch/kleis/lcaplugin/core/math/MathUtilsKtTest.kt new file mode 100644 index 000000000..17a51bf6c --- /dev/null +++ b/src/test/kotlin/ch/kleis/lcaplugin/core/math/MathUtilsKtTest.kt @@ -0,0 +1,128 @@ +package ch.kleis.lcaplugin.core.math + +import ch.kleis.lcaplugin.core.math.MatrixFixture.Companion.makeEjmlMatrix +import org.jetbrains.kotlinx.multik.api.ExperimentalMultikApi +import org.jetbrains.kotlinx.multik.api.createAlignedNDArray +import org.jetbrains.kotlinx.multik.api.mk +import org.jetbrains.kotlinx.multik.ndarray.data.get +import org.junit.Test +import kotlin.test.assertEquals + + +@OptIn(ExperimentalMultikApi::class) +class MathUtilsKtTest { + @Test + fun test_d2Ejml() { + // given + val m = mk.createAlignedNDArray( + arrayOf( + arrayOf(1.0, 2.0), + arrayOf(3.0, 4.0), + ) + ) + + // when + val actual = d2Ejml(m) + + // then + val expected = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 2.0, + 3.0, 4.0, + ), + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j -> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun test_ejmlD2() { + // given + val m = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 2.0, + 3.0, 4.0, + ), + ) + + // when + val actual = ejmlD2(m) + + // then + val expected = mk.createAlignedNDArray( + arrayOf( + arrayOf(1.0, 2.0), + arrayOf(3.0, 4.0), + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j -> + assertEquals(expected[i, j], actual[i, j]) + } + } + } + + + @Test + fun test_d3Ejml() { + // given + val m = mk.createAlignedNDArray( + arrayOf( + arrayOf(arrayOf(1.0, 2.0), arrayOf(3.0, 4.0)), + arrayOf(arrayOf(5.0, 6.0), arrayOf(7.0, 8.0)), + ) + ) + + // when + val actual = d3Ejml(m) + + // then + val expected = makeEjmlMatrix( + 2, 4, + arrayOf( + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + ), + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j -> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun test_ejmlD3() { + // given + val m = makeEjmlMatrix( + 2, 4, + arrayOf( + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + ), + ) + + // when + val actual = ejmlD3(m, 2) + + // then + val expected = mk.createAlignedNDArray( + arrayOf( + arrayOf(arrayOf(1.0, 2.0), arrayOf(3.0, 4.0)), + arrayOf(arrayOf(5.0, 6.0), arrayOf(7.0, 8.0)), + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j -> + IntRange(0, 1).forEach { k -> + assertEquals(expected[i, j, k], actual[i, j, k]) + } + } + } + } +} diff --git a/src/test/kotlin/ch/kleis/lcaplugin/core/math/MatrixFixture.kt b/src/test/kotlin/ch/kleis/lcaplugin/core/math/MatrixFixture.kt index fa6ee7402..8fff557f8 100644 --- a/src/test/kotlin/ch/kleis/lcaplugin/core/math/MatrixFixture.kt +++ b/src/test/kotlin/ch/kleis/lcaplugin/core/math/MatrixFixture.kt @@ -9,8 +9,8 @@ class MatrixFixture { val a = EJMLMatrixFactory.INSTANCE.zero(rows, cols) for (row in 0 until rows) { for (col in 0 until cols) { - if(data[cols * row + col] != 0.0) { - a.add(row, col, data[cols * row + col]) + if (data[cols * row + col] != 0.0) { + a.set(row, col, data[cols * row + col]) } } } @@ -21,12 +21,22 @@ class MatrixFixture { val a = DualMatrix.zeros(rows, cols, 0) for (row in 0 until rows) { for (col in 0 until cols) { - if(data[cols * row + col] != 0.0) { - a.add(row, col, DualNumber.constant(data[cols * row + col])) + if (data[cols * row + col] != 0.0) { + a.set(row, col, DualNumber.constant(data[cols * row + col])) } } } return a } + + fun makeDualMatrix(rows: Int, cols: Int, nParams: Int, data: Array): DualMatrix { + val a = DualMatrix.zeros(rows, cols, nParams) + for (row in 0 until rows) { + for (col in 0 until cols) { + a.set(row, col, data[cols * row + col]) + } + } + return a + } } } diff --git a/src/test/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrixTest.kt b/src/test/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrixTest.kt new file mode 100644 index 000000000..ae56d6fd1 --- /dev/null +++ b/src/test/kotlin/ch/kleis/lcaplugin/core/math/ejml/EJMLMatrixTest.kt @@ -0,0 +1,323 @@ +package ch.kleis.lcaplugin.core.math.ejml + +import ch.kleis.lcaplugin.core.math.MatrixFixture.Companion.makeEjmlMatrix +import org.junit.Test +import kotlin.test.assertEquals + +class EJMLMatrixTest { + + @Test + fun value() { + // given + val m = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = m.value(1, 2) + + // then + assertEquals(6.0, actual) + } + + @Test + fun minus() { + // given + val left = makeEjmlMatrix( + 2, 3, + arrayOf( + 6.0, 5.0, 4.0, + 3.0, 2.0, 1.0, + ) + ) + val right = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = left - right + + // then + val expected = makeEjmlMatrix( + 2, 3, + arrayOf( + 5.0, 3.0, 1.0, + -1.0, -3.0, -5.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 2).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun plus() { + // given + val left = makeEjmlMatrix( + 2, 3, + arrayOf( + 6.0, 5.0, 4.0, + 3.0, 2.0, 1.0, + ) + ) + val right = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = left + right + + // then + val expected = makeEjmlMatrix( + 2, 3, + arrayOf( + 7.0, 7.0, 7.0, + 7.0, 7.0, 7.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 2).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun matDiv() { + // given + val lhs = makeEjmlMatrix( + 2, 2, + arrayOf( + 2.0, 0.0, + 0.0, 4.0, + ) + ) + val rhs = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = rhs.matDiv(lhs)!! + + // then + val expected = makeEjmlMatrix( + 2, 3, + arrayOf( + 0.5, 1.0, 1.5, + 1.0, 1.25, 1.5, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 2).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun matMul() { + // given + val m = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 2.0, + 3.0, 4.0, + ) + ) + val x = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 1.0, 2.0, 3.0, + ) + ) + + // when + val actual = m.matMul(x) + + // then + val expected = makeEjmlMatrix( + 2, 3, + arrayOf( + 3.0, 6.0, 9.0, + 7.0, 14.0, 21.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 2).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun add() { + // given + val m = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 2.0, + 3.0, 4.0, + ) + ) + + // when + m.add(0, 1, 1.0) + + // then + val expected = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 3.0, + 3.0, 4.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j-> + assertEquals(expected.value(i, j), m.value(i, j)) + } + } + } + + @Test + fun set() { + // given + val m = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, 2.0, + 3.0, 4.0, + ) + ) + + // when + m.set(0, 1, -1.0) + + // then + val expected = makeEjmlMatrix( + 2, 2, + arrayOf( + 1.0, -1.0, + 3.0, 4.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 1).forEach { j-> + assertEquals(expected.value(i, j), m.value(i, j)) + } + } + } + + @Test + fun negate() { + // given + val m = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = m.negate() + + // then + val expected = makeEjmlMatrix( + 2, 3, + arrayOf( + -1.0, -2.0, -3.0, + -4.0, -5.0, -6.0, + ) + ) + IntRange(0, 1).forEach { i -> + IntRange(0, 2).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun transpose() { + // given + val m = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = m.transpose() + + // then + val expected = makeEjmlMatrix( + 3, 2, + arrayOf( + 1.0, 4.0, + 2.0, 5.0, + 3.0, 6.0, + ) + ) + IntRange(0, 2).forEach { i -> + IntRange(0, 1).forEach { j-> + assertEquals(expected.value(i, j), actual.value(i, j)) + } + } + } + + @Test + fun rowDim() { + // given + val m = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = m.rowDim() + + // then + assertEquals(2, actual) + } + + @Test + fun colDim() { + // given + val m = makeEjmlMatrix( + 2, 3, + arrayOf( + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + ) + ) + + // when + val actual = m.colDim() + + // then + assertEquals(3, actual) + } +} diff --git a/src/test/kotlin/ch/kleis/lcaplugin/core/matrix/ImpactFactorMatrixTest.kt b/src/test/kotlin/ch/kleis/lcaplugin/core/matrix/ImpactFactorMatrixTest.kt index a9326ac92..0494db58a 100644 --- a/src/test/kotlin/ch/kleis/lcaplugin/core/matrix/ImpactFactorMatrixTest.kt +++ b/src/test/kotlin/ch/kleis/lcaplugin/core/matrix/ImpactFactorMatrixTest.kt @@ -2,10 +2,8 @@ package ch.kleis.lcaplugin.core.matrix import ch.kleis.lcaplugin.core.lang.fixture.UnitFixture import ch.kleis.lcaplugin.core.lang.value.* -import ch.kleis.lcaplugin.core.math.DualMatrix import ch.kleis.lcaplugin.core.math.DualNumber -import org.jetbrains.kotlinx.multik.api.mk -import org.jetbrains.kotlinx.multik.api.zeros +import ch.kleis.lcaplugin.core.math.MatrixFixture import org.junit.Test import kotlin.test.assertEquals @@ -22,14 +20,7 @@ class ImpactFactorMatrixTest { private val input2 = ProductValue("water", literValue, null) private val inputs: IndexedCollection = IndexedCollection(listOf(input1, input2)) - private val matrix = object : DualMatrix( - mk.zeros(1, 1), - mk.zeros(1, 1, 0), - ) { - override fun value(row: Int, col: Int): DualNumber { - return DualNumber.constant(2E-3) // 2 liter in m3 - } - } + private val matrix = MatrixFixture.makeDualMatrix(1, 1, arrayOf(2e-3)) val sut = ImpactFactorMatrix(outputs, inputs, matrix) @Test