Skip to content

Commit

Permalink
v1.5.0
Browse files Browse the repository at this point in the history
- *BREAKING* Rot is now in radian
- Added R and CR
- Add -d option that saves memory
- Fixed Rot not parallel in ejml version
  • Loading branch information
dedztbh committed Nov 3, 2020
1 parent f6a4912 commit ad8320c
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 55 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Now with JBLAS! EJML is great for small matrices but for not big ones (matrices
## Usage

```
Usage: java -jar KuantumCircuitSim.jar options_list
Usage: java -jar Kuantum.jar options_list
Arguments:
input -> Input file { String }
operator -> Operator name { String }
Expand All @@ -64,7 +64,8 @@ Options:
--no_t, -q [false] -> Do not print circuit matrix in commandline after simulation if present
--sequential, -s [false] -> Use sequential instead of concurrent implementation if present
--init_state, -i [] -> Read custom initial joint state from csv if specified { String }
--binary_matrix, -b [false] -> Use binary format instead of csv for read/save circuit matrix if present (EJML version only)
--binary_matrix, -b [false] -> Use binary format instead of csv for read/save circuit matrix if present (EJML version only)
--disable_cache, -d [false] -> Disable cache to save memory (same gates will need to be recomputed every time)
--help, -h -> Usage info
```

Expand Down Expand Up @@ -99,19 +100,24 @@ Commands are case-insensitive.
- TDag i
- SqrtNot i
- SqrtNotDag i
- Rot i deg
+ Rotate qubit counterclockwise by degree, not rad
- Rot i angle
+ Rotate qubit counterclockwise by angle (in radian)
- R i phi
+ Phase shift by phi (mapping |1> to e^(i*phi)|1>)

##### Parallel Mode
You can enclose the above single-qubit gates that can run in parallel between "ParStart" and "ParEnd" commands. This can speed up the simulation a lot. Make sure you are using only above single-qubit gates and no two gates are acting on the same qubit.

##### Multi-Qubit Gates
Note: i controls j (and k, if present)
- CNot i j
- Swap i j
- CZ i j
+ Controlled Z gate
- SqrtSwap i j
+ Not implemented yet
- CR i j phi
+ Controlled phase shift
- CCNot i j k
- CSwap i j k

Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
group = "com.dedztbh"
version = "1.4.7"
version = "1.5.0"

val projectRoot = "${group}.kuantum"
val projectRootExclude = "/${projectRoot.replace('.', '/')}"
Expand All @@ -24,7 +24,7 @@ dependencies {
implementation("org.ejml:ejml-core:${ejmlVersion}")
implementation("org.ejml:ejml-zdense:${ejmlVersion}")
implementation("org.jblas:jblas:${jblasVersion}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0")
implementation("com.github.cvb941:kotlin-parallel-operations:1.3")
implementation("com.github.doyaaaaaken:kotlin-csv-jvm:0.11.1")
implementation("org.slf4j:slf4j-nop:1.7.30")
Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/com/dedztbh/kuantum/common/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ class Config(parser: ArgParser) {
shortName = "b",
description = "Use binary format instead of csv for read/save circuit matrix if present (EJML version only)"
).default(false)
val disable_cache by parser.option(
ArgType.Boolean,
shortName = "d",
description = "Disable cache to save memory (same gates will need to be recomputed every time)"
).default(false)
}
2 changes: 1 addition & 1 deletion src/main/kotlin/com/dedztbh/kuantum/common/Operator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface Operator {
companion object {
@JvmStatic
fun get(config: Config, scope: CoroutineScope, lib: String) =
Class.forName("com.dedztbh.kuantum.${lib}.operator.${if (config.sequential) "" else "P"}${config.operator}")
Class.forName("com.dedztbh.kuantum.$lib.operator.${if (config.sequential) "" else "P"}${config.operator}")
.getDeclaredConstructor(Config::class.java, CoroutineScope::class.java)
.newInstance(config, scope) as Operator
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/com/dedztbh/kuantum/common/util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,25 @@ fun allStates(n: Int) =
Array(2.0.pow(n).toInt()) {
List(n) { i -> (it shr i) and 1 }.asReversed()
}


/** Data Structure */
class TODOMap<K, V> : MutableMap<K, V> {
override val size
get() = throw NotImplementedError()
override val entries
get() = throw NotImplementedError()
override val keys
get() = throw NotImplementedError()
override val values
get() = throw NotImplementedError()

override fun containsKey(key: K): Boolean = throw NotImplementedError()
override fun containsValue(value: V): Boolean = throw NotImplementedError()
override fun get(key: K): V? = throw NotImplementedError()
override fun isEmpty(): Boolean = throw NotImplementedError()
override fun clear() = throw NotImplementedError()
override fun put(key: K, value: V): V? = throw NotImplementedError()
override fun putAll(from: Map<out K, V>) = throw NotImplementedError()
override fun remove(key: K): V? = throw NotImplementedError()
}
95 changes: 72 additions & 23 deletions src/main/kotlin/com/dedztbh/kuantum/ejml/operator/TFinder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import com.lukaskusik.coroutines.transformations.reduce.reduceParallel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import java.util.concurrent.ConcurrentHashMap
import kotlin.math.*
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
import kotlin.math.sqrt

/**
* Created by DEDZTBH on 2020/09/22.
Expand Down Expand Up @@ -48,11 +51,14 @@ open class TFinder(val config: Config, val scope: CoroutineScope) : Operator {
@JvmField
val IKronTable = (0..N).scan(I1) { it, _ -> it kron I2 }.toTypedArray()

open fun <K, V> getHashMap(): MutableMap<K, V> = HashMap()
@JvmField
val cacheEnabled = !config.disable_cache

open fun <K, V> getHashMap(): MutableMap<K, V> = if (cacheEnabled) HashMap() else TODOMap()

@JvmField
val matrix0CtrlCache: Array<MutableMap<CMatrix, CMatrix>> = Array(N) { getHashMap() }
fun get0CtrlMatrix(i: Int, mat: CMatrix, cache: Boolean = true): CMatrix {
fun get0CtrlMatrix(i: Int, mat: CMatrix, cache: Boolean = cacheEnabled): CMatrix {
if (cache) matrix0CtrlCache[i][mat]?.let { return it }
val res = IKronTable[i] kron mat kron IKronTable[N - i - 1]
if (cache) matrix0CtrlCache[i][mat] = res
Expand All @@ -63,8 +69,8 @@ open class TFinder(val config: Config, val scope: CoroutineScope) : Operator {
* http://www.sakkaris.com/tutorials/quantum_control_gates.html */
@JvmField
val matrix1CtrlCache: Array<Array<MutableMap<CMatrix, CMatrix>>> = Array(N) { Array(N) { getHashMap() } }
fun get1CtrlMatrix(i: Int, j: Int, mat: CMatrix): CMatrix {
matrix1CtrlCache[i][j][mat]?.let { return it }
fun get1CtrlMatrix(i: Int, j: Int, mat: CMatrix, cache: Boolean = cacheEnabled): CMatrix {
if (cache) matrix1CtrlCache[i][j][mat]?.let { return it }
val res = get0CtrlMatrix(i, KETBRA0) + when {
i < j -> IKronTable[i] kron KETBRA1 kron
IKronTable[j - i - 1] kron
Expand All @@ -74,65 +80,79 @@ open class TFinder(val config: Config, val scope: CoroutineScope) : Operator {
(KETBRA1 kron IKronTable[N - i - 1])
else -> throw IllegalArgumentException("Control qubit is same as affected qubit")
}
matrix1CtrlCache[i][j][mat] = res
if (cache) matrix1CtrlCache[i][j][mat] = res
return res
}

@JvmField
val ccnotCache: Array<Array<MutableMap<Int, CMatrix>>> = Array(N) { Array(N) { getHashMap() } }
fun getCCNotMatrix(i: Int, j: Int, k: Int): CMatrix {
/** using Sleator-Weinfurter construction */
ccnotCache[i][j][k]?.let { return it }
if (cacheEnabled) ccnotCache[i][j][k]?.let { return it }
val cnotij = get1CtrlMatrix(i, j, NOT)
val res = get1CtrlMatrix(i, k, SQRT_NOT) *
cnotij *
get1CtrlMatrix(j, k, SQRT_NOT_DAG) *
cnotij *
get1CtrlMatrix(j, k, SQRT_NOT)
ccnotCache[i][j][k] = res
if (cacheEnabled) ccnotCache[i][j][k] = res
return res
}

@JvmField
val rotCache: Array<MutableMap<Double, CMatrix>> = Array(N) { getHashMap() }
fun getRotMatrix(i: Int, d: Double): CMatrix {
rotCache[i][d]?.let { return it }
val rad = d * PI / 180.0
val sine = sin(rad)
val cosine = cos(rad)
fun getRotMatrix(i: Int, angle: Double): CMatrix {
if (cacheEnabled) rotCache[i][angle]?.let { return it }
val sine = sin(angle)
val cosine = cos(angle)
val rotMat = CMatrix(
arrayOf(
doubleArrayOf(cosine, 0.0, -sine, 0.0),
doubleArrayOf(sine, 0.0, cosine, 0.0)
)
)
return get0CtrlMatrix(i, rotMat, false).also {
rotCache[i][d] = it
if (cacheEnabled) rotCache[i][angle] = it
}
}

@JvmField
val RCache: Array<MutableMap<Double, CMatrix>> = Array(N) { getHashMap() }
fun getRMatrix(i: Int, phi: Double): CMatrix {
if (cacheEnabled) RCache[i][phi]?.let { return it }
val RMat = CMatrix(
arrayOf(
doubleArrayOf(1.0, 0.0, 0.0, 0.0),
doubleArrayOf(0.0, 0.0, cos(phi), sin(phi))
)
)
return get0CtrlMatrix(i, RMat, false).also {
if (cacheEnabled) RCache[i][phi] = it
}
}

@JvmField
val swapCache: Array<MutableMap<Int, CMatrix>> = Array(N) { getHashMap() }
fun getSwapMatrix(i: Int, j: Int): CMatrix {
swapCache[i][j]?.let { return it }
if (cacheEnabled) swapCache[i][j]?.let { return it }
/** https://algassert.com/post/1717
* Swap implemented with 3 CNots */
val cnot0 = get1CtrlMatrix(i, j, NOT)
return (cnot0 * get1CtrlMatrix(j, i, NOT) * cnot0).also {
swapCache[i][j] = it
if (cacheEnabled) swapCache[i][j] = it
}
}

@JvmField
val cswapCache: Array<Array<MutableMap<Int, CMatrix>>> = Array(N) { Array(N) { getHashMap() } }
fun getCSwapMatrix(i: Int, j: Int, k: Int): CMatrix {
cswapCache[i][j][k]?.let { return it }
if (cacheEnabled) cswapCache[i][j][k]?.let { return it }
/** https://quantumcomputing.stackexchange.com/
* questions/9342/how-to-implement-a-fredkin
* -gate-using-toffoli-and-cnots */
val cnotkj = get1CtrlMatrix(k, j, NOT)
return (cnotkj * getCCNotMatrix(i, j, k) * cnotkj).also {
cswapCache[i][j][k] = it
if (cacheEnabled) cswapCache[i][j][k] = it
}
}

Expand Down Expand Up @@ -169,8 +189,24 @@ open class TFinder(val config: Config, val scope: CoroutineScope) : Operator {
// "SQRTSWAP" -> {
// TODO: Implement SqrtSwap
// }
"ROT" -> getRotMatrix(i, readDouble())
"ROT" -> {
val angle = readDouble()
if (parallelMode) {
parallelMatrices[i] = getRotMatrix(i, angle)
return@checkParAndWhen
}
getRotMatrix(i, angle)
}
"R" -> {
val phi = readDouble()
if (parallelMode) {
parallelMatrices[i] = getRMatrix(i, phi)
return@checkParAndWhen
}
getRMatrix(i, phi)
}
"CZ" -> get1CtrlMatrix(i, readInt(), Z)
"CR" -> get1CtrlMatrix(i, readInt(), getRMatrix(i, readDouble()), false)
else -> map0Ctrl(cmd).let {
if (it != null) {
if (parallelMode) {
Expand Down Expand Up @@ -215,7 +251,7 @@ open class PTFinder(config: Config, scope: CoroutineScope) : TFinder(config, sco
@JvmField
var reversedNewOps = mutableListOf(scope.async { opMatrix })

override fun <K, V> getHashMap(): MutableMap<K, V> = ConcurrentHashMap()
override fun <K, V> getHashMap(): MutableMap<K, V> = if (cacheEnabled) ConcurrentHashMap() else TODOMap()

suspend inline fun checkParAndWhenConcurrent(cmd: String, block: () -> Unit) =
when (cmd) {
Expand Down Expand Up @@ -268,17 +304,30 @@ open class PTFinder(config: Config, scope: CoroutineScope) : TFinder(config, sco
// TODO: Implement SqrtSwap
// }
"ROT" -> {
val deg = readDouble()
val angle = readDouble()
if (parallelMode) {
parallelMatrices[i] = getRotMatrix(i, deg)
parallelMatrices[i] = getRotMatrix(i, angle)
return@checkParAndWhenConcurrent
}
async { getRotMatrix(i, deg) }
async { getRotMatrix(i, angle) }
}
"R" -> {
val phi = readDouble()
if (parallelMode) {
parallelMatrices[i] = getRMatrix(i, phi)
return@checkParAndWhenConcurrent
}
async { getRMatrix(i, phi) }
}
"CZ" -> {
val j = readInt()
async { get1CtrlMatrix(i, j, Z) }
}
"CR" -> {
val j = readInt()
val phi = readDouble()
async { get1CtrlMatrix(i, j, getRMatrix(i, phi), false) }
}
else -> map0Ctrl(cmd).let {
if (it != null) {
if (parallelMode) {
Expand Down
Loading

0 comments on commit ad8320c

Please sign in to comment.