From 202d9975030af4dad8e8d3f5ba7b85f70bac042a Mon Sep 17 00:00:00 2001 From: DEDZTBH Date: Fri, 18 Sep 2020 16:16:49 +0800 Subject: [PATCH] v1.1 Changed matrix library to EJML --- .gitignore | 2 + build.gradle.kts | 14 ++-- src/main/kotlin/Main.kt | 2 + src/main/kotlin/operator/Prob0Init.kt | 58 +++++++++------- src/main/kotlin/operator/ProbAllInit.kt | 92 +++++++++++++------------ src/main/kotlin/operator/ProbFinder.kt | 37 +++++----- src/main/kotlin/operator/Tester.kt | 10 +-- src/main/kotlin/{util.kt => util/io.kt} | 10 +-- src/main/kotlin/util/matrix.kt | 25 +++++++ 9 files changed, 144 insertions(+), 106 deletions(-) rename src/main/kotlin/{util.kt => util/io.kt} (70%) create mode 100644 src/main/kotlin/util/matrix.kt diff --git a/.gitignore b/.gitignore index 11ab10c..566b31f 100644 --- a/.gitignore +++ b/.gitignore @@ -149,5 +149,7 @@ CMU_Coin-flipping_Experience.iws CMU_Coin-flipping_Experience.main.iml CMU_Coin-flipping_Experience.test.iml gradle/ +gradlew +gradlew.bat input.txt \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 2f54022..8319f67 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,24 +1,23 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "1.4.10" id("com.github.johnrengelman.shadow") version "6.0.0" } group = "com.dedztbh" -version = "1.0-SNAPSHOT" +version = "1.1" -val dl4jVersion = "1.0.0-beta7" +val ejmlVersion = "0.39" repositories { mavenCentral() - maven("https://dl.bintray.com/mipt-npm/scientifik") } dependencies { - implementation("org.deeplearning4j:deeplearning4j-core:${dl4jVersion}") - implementation("org.nd4j:nd4j-native-platform:${dl4jVersion}") - implementation("org.slf4j:slf4j-nop:1.7.13") + implementation("org.ejml:ejml-core:${ejmlVersion}") + implementation("org.ejml:ejml-kotlin:${ejmlVersion}") + implementation("org.ejml:ejml-fdense:${ejmlVersion}") // testImplementation(kotlin("test-junit")) } tasks.withType() { @@ -32,6 +31,7 @@ tasks { manifest { attributes(mapOf("Main-Class" to "MainKt")) } + minimize() } } diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 034861b..e72842d 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,4 +1,6 @@ import operator.Operator +import util.read +import util.reader import java.io.File /** diff --git a/src/main/kotlin/operator/Prob0Init.kt b/src/main/kotlin/operator/Prob0Init.kt index efd3603..0875c50 100644 --- a/src/main/kotlin/operator/Prob0Init.kt +++ b/src/main/kotlin/operator/Prob0Init.kt @@ -1,43 +1,52 @@ package operator -import allStates -import org.nd4j.linalg.api.ndarray.INDArray +import org.ejml.data.FMatrixRMaj +import org.ejml.dense.row.CommonOps_FDRM +import org.ejml.kotlin.plusAssign +import kotlin.math.pow /** * Created by DEDZTBH on 2020/09/15. * Project CMU_Coin-flipping_Experience */ + +fun allStates(n: Int) = + Array(2.0.pow(n).toInt()) { + IntArray(n) { i -> (it shr i) and 1 }.apply { reverse() } + } + class Prob0Init(N: Int) : ProbFinder(N) { - fun eval(probs: INDArray): INDArray { + fun eval(probs: FMatrixRMaj): FMatrixRMaj { operations.forEach { it.apply { when (this) { - is Matrix -> { - probs.muli(opVec) - probs.addi(opBias) + is MatrixOp -> { + CommonOps_FDRM.elementMult(probs, opVec) + probs += opBias } is CNot -> { - val x = probs.getDouble(i) - val y = probs.getDouble(j) - probs.putScalar(j.toLong(), (1.0 - x) * y + x * (1.0 - y)) + val x = probs[i] + val y = probs[j] + probs[j] = (1f - x) * y + x * (1f - y) } is CSwap -> { - val x = probs.getDouble(i) - val y = probs.getDouble(j) - val z = probs.getDouble(k) - probs.putScalar(j.toLong(), (1.0 - x) * y + x * z) - probs.putScalar(k.toLong(), (1.0 - x) * z + x * y) + val x = probs[i] + val y = probs[j] + val z = probs[k] + val xFlip = 1f - x + probs[j] = xFlip * y + x * z + probs[k] = xFlip * z + x * y } is CCNot -> { - val x = probs.getDouble(i) - val y = probs.getDouble(j) - val z = probs.getDouble(k) - val probBoth0 = (1.0 - x) * (1.0 - y) - probs.putScalar(k.toLong(), probBoth0 * z + (1.0 - probBoth0) * (1.0 - z)) + val x = probs[i] + val y = probs[j] + val z = probs[k] + val probBoth0 = (1f - x) * (1f - y) + probs[k] = probBoth0 * z + (1f - probBoth0) * (1f - z) } is Gen1Bit -> { - val x = probs.getDouble(i) - probs.putScalar(i.toLong(), x * (1.0 - q) * x + (1.0 - x) * (p + (1.0 - p) * x)) + val x = probs[i] + probs[i] = x * (1f - q) * x + (1f - x) * (p + (1f - p) * x) } } } @@ -48,11 +57,12 @@ class Prob0Init(N: Int) : ProbFinder(N) { override fun printResult() { val probs = eval(getZeroVec()) allStates(N).forEach { endState -> - var prob = 1.0 + var prob = 1f endState.forEach { i -> - prob *= if (i > 0) probs.getDouble(i) else (1.0 - probs.getDouble(i)) + val x = probs[i] + prob *= if (i > 0) x else 1f - x } - println("Pr[%s] = %.9f".format(endState.joinToString(","), prob)) + println("Pr[%s] = %.10f".format(endState.joinToString(","), prob)) } } } \ No newline at end of file diff --git a/src/main/kotlin/operator/ProbAllInit.kt b/src/main/kotlin/operator/ProbAllInit.kt index bd0d122..3f60689 100644 --- a/src/main/kotlin/operator/ProbAllInit.kt +++ b/src/main/kotlin/operator/ProbAllInit.kt @@ -1,69 +1,75 @@ package operator -import allStates -import org.nd4j.linalg.api.buffer.DataType -import org.nd4j.linalg.api.ndarray.INDArray -import org.nd4j.linalg.factory.Nd4j -import org.nd4j.linalg.string.NDArrayStrings +import org.ejml.data.FMatrixRMaj +import org.ejml.dense.row.CommonOps_FDRM +import org.ejml.kotlin.minus +import org.ejml.kotlin.plus +import org.ejml.kotlin.plusAssign +import org.ejml.kotlin.times +import util.elemMult +import util.getColumn +import util.putColumn +import util.scale +import kotlin.math.pow /** * Created by DEDZTBH on 2020/09/15. * Project CMU_Coin-flipping_Experience */ + +fun allStatesFloat(n: Int) = + Array(2.0.pow(n).toInt()) { + FloatArray(n) { i -> ((it shr i) and 1).toFloat() }.apply { reverse() } + } + class ProbAllInit(N: Int) : ProbFinder(N) { - fun eval(probs: INDArray): INDArray { + fun eval(probs: FMatrixRMaj): FMatrixRMaj { + val broadcastVec = + FMatrixRMaj(probs.numRows, + 1, + true, + *FloatArray(probs.numRows) { 1f }) operations.forEach { it.apply { when (this) { - is Matrix -> { - probs.muliRowVector(opVec) - probs.addiRowVector(opBias) + is MatrixOp -> { + CommonOps_FDRM.elementMult(probs, broadcastVec * opVec) + probs += broadcastVec * opBias } is CNot -> { - val i = i.toLong() - val j = j.toLong() - val x = probs.getColumn(i) - val y = probs.getColumn(j) + val x = probs getColumn i + val y = probs getColumn j //(1.0 - x) * y + x * (1.0 - y) - val newCol = x.mul(y).mul(-2.0).add(x).add(y) - probs.putColumn(this.j, newCol) + probs.putColumn(j, (x elemMult y scale -2f) + x + y) } is CSwap -> { - val i = i.toLong() - val j = j.toLong() - val k = k.toLong() - val x = probs.getColumn(i) - val y = probs.getColumn(j) - val z = probs.getColumn(k) - val xy = x.mul(y) - val xz = x.mul(z) + val x = probs getColumn i + val y = probs getColumn j + val z = probs getColumn k + val xy = x elemMult y + val xz = x elemMult z //(1.0 - x) * y + x * z - probs.putColumn(this.j, y.sub(xy).add(xz)) + probs.putColumn(j, y - xy + xz) //(1.0 - x) * z + x * y - probs.putColumn(this.k, z.sub(xz).add(xy)) + probs.putColumn(k, z - xz + xy) } is CCNot -> { - val i = i.toLong() - val j = j.toLong() - val k = k.toLong() - val x = probs.getColumn(i) - val y = probs.getColumn(j) - val z = probs.getColumn(k) + val x = probs getColumn i + val y = probs getColumn j + val z = probs getColumn k //(1.0 - x) * (1.0 - y) - val probBoth0 = x.mul(y).sub(x).sub(y).add(1.0) + val probBoth0 = (x elemMult y) - x - y + 1f //probBoth0 * z + (1.0 - probBoth0) * (1.0 - z) - probs.putColumn(this.k, probBoth0.mul(z).mul(2.0).sub(probBoth0).sub(z).add(1.0)) + probs.putColumn(k, (probBoth0 elemMult z scale 2f) - probBoth0 - z + 1f) } is Gen1Bit -> { - val i = i.toLong() - val x = probs.getColumn(i) - val xx = x.mul(x) - val p_comp = 1.0 - p + val x = probs getColumn i + val xx = x elemMult x + val pComp = 1f - p + //x * (1.0 - q) * x + (1.0 - x) * (p + (1.0 - p) * x) + //= xx(1-q) + p + (1-p)x - xp - xx(1-p) probs.putColumn( - this.i, - //x * (1.0 - q) * x + (1.0 - x) * (p + (1.0 - p) * x) - //= xx(1-q) + p + (1-p)x - xp - xx(1-p) - xx.mul(1.0 - q).add(p).add(x.mul(p_comp)).sub(x.mul(p)).sub(xx.mul(p_comp)) + i, (xx scale 1f - q) + p + (x scale pComp) - (x scale p) - (xx scale pComp) ) } } @@ -75,8 +81,8 @@ class ProbAllInit(N: Int) : ProbFinder(N) { override fun printResult() { val probs = eval( // a 2^n by n matrix - Nd4j.create(allStates(N)).castTo(DataType.DOUBLE) + FMatrixRMaj(allStatesFloat(N)) ) - println(probs.toString(NDArrayStrings(Long.MAX_VALUE, 9))) + probs.print() } } \ No newline at end of file diff --git a/src/main/kotlin/operator/ProbFinder.kt b/src/main/kotlin/operator/ProbFinder.kt index a67b43a..20075fa 100644 --- a/src/main/kotlin/operator/ProbFinder.kt +++ b/src/main/kotlin/operator/ProbFinder.kt @@ -1,28 +1,26 @@ package operator -import org.nd4j.linalg.api.buffer.DataType -import org.nd4j.linalg.api.ndarray.INDArray -import org.nd4j.linalg.factory.Nd4j -import readDouble -import readInt +import org.ejml.data.FMatrixRMaj +import util.readFloat +import util.readInt sealed class Operation -data class Matrix(val opVec: INDArray, val opBias: INDArray) : Operation() +data class MatrixOp(val opVec: FMatrixRMaj, val opBias: FMatrixRMaj) : Operation() data class CNot(val i: Int, val j: Int) : Operation() data class CSwap(val i: Int, val j: Int, val k: Int) : Operation() data class CCNot(val i: Int, val j: Int, val k: Int) : Operation() -data class Gen1Bit(val i: Int, val p: Double, val q: Double) : Operation() +data class Gen1Bit(val i: Int, val p: Float, val q: Float) : Operation() /** * Created by DEDZTBH on 2020/09/15. * Project CMU_Coin-flipping_Experience */ abstract class ProbFinder(val N: Int) : Operator { - fun getOneVec(): INDArray = Nd4j.ones(1, N).castTo(DataType.DOUBLE) - fun getZeroVec(): INDArray = Nd4j.zeros(1, N).castTo(DataType.DOUBLE) + fun getOneVec(): FMatrixRMaj = FMatrixRMaj(arrayOf(FloatArray(N) { 1f })) + fun getZeroVec(): FMatrixRMaj = FMatrixRMaj(1, N) fun saveMatrix() { if (matrixDirty) { - operations.add(Matrix(opVec, opBias)) + operations.add(MatrixOp(opVec, opBias)) opVec = getOneVec() opBias = getZeroVec() matrixDirty = false @@ -37,16 +35,15 @@ abstract class ProbFinder(val N: Int) : Operator { override fun runCmd(cmd: String): Int { val i = readInt() - val il = i.toLong() when (cmd) { "Flip" -> { - opVec.putScalar(il, 0.0) - opBias.putScalar(il, 0.5) + opVec[i] = 0f + opBias[i] = 0.5f matrixDirty = true } "Not" -> { - opVec.putScalar(il, -opVec.getDouble(il)) - opBias.putScalar(il, 1.0 - opBias.getDouble(il)) + opVec[i] *= -1f + opBias[i] = 1f - opBias[i] matrixDirty = true } "CNot" -> { // cannot represent in matrix op @@ -67,14 +64,14 @@ abstract class ProbFinder(val N: Int) : Operator { operations.add(CCNot(i, j, k)) } "GenFlip" -> { - val j = readDouble() - opVec.putScalar(il, 0.0) - opBias.putScalar(il, j) + val j = readFloat() + opVec[i] = 0f + opBias[i] = j matrixDirty = true } "Gen1Bit" -> { // cannot represent in matrix op - val p = readDouble() - val q = readDouble() + val p = readFloat() + val q = readFloat() saveMatrix() operations.add(Gen1Bit(i, p, q)) } diff --git a/src/main/kotlin/operator/Tester.kt b/src/main/kotlin/operator/Tester.kt index 7c875c0..67e6982 100644 --- a/src/main/kotlin/operator/Tester.kt +++ b/src/main/kotlin/operator/Tester.kt @@ -1,7 +1,7 @@ package operator -import readDouble -import readInt +import util.readFloat +import util.readInt import kotlin.random.Random /** @@ -32,10 +32,10 @@ class Tester(N: Int) : Operator { val k = readInt() if (coins[i] or coins[j] != 0) coins flip k } - "GenFlip" -> coins[i] = if (Random.nextDouble() < readDouble()) 1 else 0 + "GenFlip" -> coins[i] = if (Random.nextDouble() < readFloat()) 1 else 0 "Gen1Bit" -> { - val p = readDouble() - val q = readDouble() + val p = readFloat() + val q = readFloat() if (coins[i] == 0) { if (Random.nextDouble() < p) coins[i] = 1 } else diff --git a/src/main/kotlin/util.kt b/src/main/kotlin/util/io.kt similarity index 70% rename from src/main/kotlin/util.kt rename to src/main/kotlin/util/io.kt index a8cdb21..d95bf77 100644 --- a/src/main/kotlin/util.kt +++ b/src/main/kotlin/util/io.kt @@ -1,6 +1,7 @@ +package util + import java.io.BufferedReader import java.util.* -import kotlin.math.pow /** * Created by DEDZTBH on 2020/09/15. @@ -16,11 +17,6 @@ fun read(): String { } fun readInt() = read().toInt() -fun readDouble() = read().toDouble() - -fun allStates(n: Int) = - Array(2.0.pow(n).toInt()) { - IntArray(n) { i -> (it shr i) and 1 }.apply { reverse() } - } +fun readFloat() = read().toFloat() lateinit var reader: BufferedReader \ No newline at end of file diff --git a/src/main/kotlin/util/matrix.kt b/src/main/kotlin/util/matrix.kt new file mode 100644 index 0000000..b9ecf81 --- /dev/null +++ b/src/main/kotlin/util/matrix.kt @@ -0,0 +1,25 @@ +package util + +import org.ejml.data.FMatrixRMaj +import org.ejml.dense.row.CommonOps_FDRM + +/** + * Created by DEDZTBH on 2020/09/18. + * Project CMU_Coin-flipping_Experience + */ + +infix fun FMatrixRMaj.getColumn(i: Int) = CommonOps_FDRM.extractColumn(this, i, null) + +fun FMatrixRMaj.putColumn(i: Int, col: FMatrixRMaj) = col.data.forEachIndexed { rowi, fl -> set(rowi, i, fl) } + +infix fun FMatrixRMaj.elemMult(other: FMatrixRMaj) = run { + val c = createLike() + CommonOps_FDRM.elementMult(this, other, c) + c +} + +infix fun FMatrixRMaj.scale(d: Float) = run { + val res = createLike() + CommonOps_FDRM.scale(d, this, res) + res +} \ No newline at end of file