From 5aa995cc0841d94b651113da0821ffc8f9d46961 Mon Sep 17 00:00:00 2001 From: Vasily Vasinov Date: Mon, 30 Sep 2019 11:28:08 -0600 Subject: [PATCH 1/5] Rename `GateGroup` to `CompositeGate` --- src/main/scala/scotty/quantum/Circuit.scala | 4 +-- src/main/scala/scotty/quantum/gate/Gate.scala | 2 +- .../scotty/simulator/QuantumSimulator.scala | 26 +++++++++---------- .../scala/scotty/simulator/DefGateSpec.scala | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/scala/scotty/quantum/Circuit.scala b/src/main/scala/scotty/quantum/Circuit.scala index 660ca47..6a26d86 100644 --- a/src/main/scala/scotty/quantum/Circuit.scala +++ b/src/main/scala/scotty/quantum/Circuit.scala @@ -1,7 +1,7 @@ package scotty.quantum import scotty.ErrorMessage -import scotty.quantum.gate.{GateGroup, Gate} +import scotty.quantum.gate.{CompositeGate, Gate} case class Circuit(register: QubitRegister, ops: Op*) { val indices: Range = 0 until register.size @@ -22,7 +22,7 @@ case class Circuit(register: QubitRegister, ops: Op*) { def isValid: Boolean = register.size >= Circuit.qubitCountFromOps(ops) def gates: Seq[Gate] = ops.collect { - case cc: GateGroup => cc.gates + case cg: CompositeGate => cg.gates case g: Gate => Seq(g) }.flatten diff --git a/src/main/scala/scotty/quantum/gate/Gate.scala b/src/main/scala/scotty/quantum/gate/Gate.scala index ad92aeb..70764aa 100644 --- a/src/main/scala/scotty/quantum/gate/Gate.scala +++ b/src/main/scala/scotty/quantum/gate/Gate.scala @@ -68,7 +68,7 @@ case class Dagger(target: TargetGate) extends TargetGate { val matrixGen: MatrixGen = _ => MatrixWrapper.conjugateTranspose(target.matrix) } -case class GateGroup(gates: Gate*) extends Gate { +case class CompositeGate(gates: Gate*) extends Gate { val indices: Seq[Int] = gates.flatMap(g => g.indices).distinct } diff --git a/src/main/scala/scotty/simulator/QuantumSimulator.scala b/src/main/scala/scotty/simulator/QuantumSimulator.scala index c4971dd..117d7ee 100644 --- a/src/main/scala/scotty/simulator/QuantumSimulator.scala +++ b/src/main/scala/scotty/simulator/QuantumSimulator.scala @@ -62,20 +62,20 @@ case class QuantumSimulator(ec: Option[ExecutionContext], random: Random) extend def applyGate(state: Vector, gate: Gate): Unit = applyGate(parIndices(state.length / 2), state, gate) def applyGate(iterator: ParIterable[Int], state: Vector, gate: Gate): Unit = gate match { - case g: SWAP => applyGateGroup(iterator, state, QuantumSimulator.swap.apply(g.index1, g.index2)) - case g: CSWAP => applyGateGroup(iterator, state, QuantumSimulator.cswap.apply(g.controlIndex, g.index1, g.index2)) - case g: ISWAP => applyGateGroup(iterator, state, QuantumSimulator.iswap.apply(g.index1, g.index2)) - case g: PSWAP => applyGateGroup(iterator, state, QuantumSimulator.pswap.apply(g.phi, g.index1, g.index2)) + case g: SWAP => applyComposite(iterator, state, QuantumSimulator.swap.apply(g.index1, g.index2)) + case g: CSWAP => applyComposite(iterator, state, QuantumSimulator.cswap.apply(g.controlIndex, g.index1, g.index2)) + case g: ISWAP => applyComposite(iterator, state, QuantumSimulator.iswap.apply(g.index1, g.index2)) + case g: PSWAP => applyComposite(iterator, state, QuantumSimulator.pswap.apply(g.phi, g.index1, g.index2)) case g: CPHASE00 => - applyGateGroup(iterator, state, QuantumSimulator.cphase00.apply(g.phi, g.controlIndex, g.targetIndex)) + applyComposite(iterator, state, QuantumSimulator.cphase00.apply(g.phi, g.controlIndex, g.targetIndex)) case g: CPHASE01 => - applyGateGroup(iterator, state, QuantumSimulator.cphase01.apply(g.phi, g.controlIndex, g.targetIndex)) + applyComposite(iterator, state, QuantumSimulator.cphase01.apply(g.phi, g.controlIndex, g.targetIndex)) case control: ControlGate => applyControlGate(iterator, state, control) case target: TargetGate => applyTargetGate(iterator, state, target.index, target.matrix) case _ => ??? } - def applyGateGroup(iterator: ParIterable[Int], state: Vector, gg: GateGroup): Unit = { + def applyComposite(iterator: ParIterable[Int], state: Vector, gg: CompositeGate): Unit = { gg.gates.foreach(g => applyGate(iterator, state, g)) } @@ -193,12 +193,12 @@ case class QuantumSimulator(ec: Option[ExecutionContext], random: Random) extend } object QuantumSimulator { - val swap = (i0: Int, i1: Int) => GateGroup(CNOT(i0, i1), CNOT(i1, i0), CNOT(i0, i1)) - val cswap = (i0: Int, i1: Int, i2: Int) => GateGroup(CNOT(i2, i1), CCNOT(i0, i1, i2), CNOT(i2, i1)) - val iswap = (i0: Int, i1: Int) => GateGroup(SWAP(i0, i1), S(i0), S(i1), CPHASE(Math.PI / 2, i0, i1)) - val pswap = (phi: Double, i0: Int, i1: Int) => GateGroup(SWAP(i0, i1), PHASE(phi, i0), PHASE(phi, i1), CPHASE(phi, i0, i1)) - val cphase00 = (phi: Double, i0: Int, i1: Int) => GateGroup(X(i0), CPHASE10(phi, i0, i1), X(i0)) - val cphase01 = (phi: Double, i0: Int, i1: Int) => GateGroup(X(i0), CPHASE(phi, i0, i1), X(i0)) + val swap = (i0: Int, i1: Int) => CompositeGate(CNOT(i0, i1), CNOT(i1, i0), CNOT(i0, i1)) + val cswap = (i0: Int, i1: Int, i2: Int) => CompositeGate(CNOT(i2, i1), CCNOT(i0, i1, i2), CNOT(i2, i1)) + val iswap = (i0: Int, i1: Int) => CompositeGate(SWAP(i0, i1), S(i0), S(i1), CPHASE(Math.PI / 2, i0, i1)) + val pswap = (phi: Double, i0: Int, i1: Int) => CompositeGate(SWAP(i0, i1), PHASE(phi, i0), PHASE(phi, i1), CPHASE(phi, i0, i1)) + val cphase00 = (phi: Double, i0: Int, i1: Int) => CompositeGate(X(i0), CPHASE10(phi, i0, i1), X(i0)) + val cphase01 = (phi: Double, i0: Int, i1: Int) => CompositeGate(X(i0), CPHASE(phi, i0, i1), X(i0)) def apply(): QuantumSimulator = QuantumSimulator(None, new Random()) diff --git a/src/test/scala/scotty/simulator/DefGateSpec.scala b/src/test/scala/scotty/simulator/DefGateSpec.scala index d6448cd..aecd604 100644 --- a/src/test/scala/scotty/simulator/DefGateSpec.scala +++ b/src/test/scala/scotty/simulator/DefGateSpec.scala @@ -3,7 +3,7 @@ package scotty.simulator import org.scalatest.FlatSpec import scotty.TestHelpers import scotty.quantum._ -import scotty.quantum.gate.{DefGate, GateGroup} +import scotty.quantum.gate.{DefGate, CompositeGate} import scotty.quantum.math.Complex class DefGateSpec extends FlatSpec with TestHelpers { From 6a5739c4b48139e83d5dcc1d7c281491eda2f0ea Mon Sep 17 00:00:00 2001 From: Vasily Vasinov Date: Mon, 30 Sep 2019 12:01:21 -0600 Subject: [PATCH 2/5] Refactor bit and qubit --- src/main/scala/scotty/quantum/Bit.scala | 12 +++++++----- src/main/scala/scotty/quantum/Qubit.scala | 20 +++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main/scala/scotty/quantum/Bit.scala b/src/main/scala/scotty/quantum/Bit.scala index 6c07304..be25ff0 100644 --- a/src/main/scala/scotty/quantum/Bit.scala +++ b/src/main/scala/scotty/quantum/Bit.scala @@ -1,5 +1,6 @@ package scotty.quantum +import scotty.quantum.QuantumContext.Vector import scotty.quantum.math.Complex import scotty.{ErrorMessage, Labeled} @@ -9,7 +10,7 @@ sealed trait Bit extends Labeled[String] { case _: Zero => Array(Complex(1), Complex(0)) } - def toFloatArray: Array[Float] = this match { + def toVector: Vector = this match { case _: One => Array(0f, 0f, 1f, 0f) case _: Zero => Array(1f, 0f, 0f, 0f) } @@ -40,10 +41,11 @@ object Bit { case _ => throw new IllegalArgumentException(ErrorMessage.IntToBit) } - def apply(value: Array[Complex]): Bit = - if (value.toSeq == Seq(Complex(1), Complex(0))) Zero() - else if (value.toSeq == Seq(Complex(0), Complex(1))) One() - else throw new IllegalArgumentException(ErrorMessage.VectorToBit) + def apply(value: Array[Complex]): Bit = value.toSeq match { + case Seq(Complex(1, 0), Complex(0, 0)) => Zero() + case Seq(Complex(0, 0), Complex(1, 0)) => One() + case _ => throw new IllegalArgumentException(ErrorMessage.VectorToBit) + } } object One { diff --git a/src/main/scala/scotty/quantum/Qubit.scala b/src/main/scala/scotty/quantum/Qubit.scala index a3465bc..228f1c2 100644 --- a/src/main/scala/scotty/quantum/Qubit.scala +++ b/src/main/scala/scotty/quantum/Qubit.scala @@ -7,7 +7,7 @@ import scotty.{ErrorMessage, Labeled} case class Qubit(a: Complex, b: Complex, label: Option[String]) extends Labeled[String] { require(Qubit.areAmplitudesValid(this), ErrorMessage.IncorrectQubitAmplitudes) - def toBasisState: Vector = Array(a.r, a.i, b.r, b.i) + def toVector: Vector = Array(a.r, a.i, b.r, b.i) def toHumanString: String = s"Qubit(${a.toString}, ${b.toString})" @@ -17,17 +17,23 @@ case class Qubit(a: Complex, b: Complex, label: Option[String]) extends Labeled[ } object Qubit { - def one(label: String): Qubit = Qubit(Complex(0), Complex(1), Some(label)) + def zero(label: Option[String]): Qubit = Qubit(Complex(1), Complex(0), label) - def one: Qubit = Qubit(Complex(0), Complex(1), None) + def zero(label: String): Qubit = one(Some(label)) - def zero(label: String): Qubit = Qubit(Complex(1), Complex(0), Some(label)) + def zero: Qubit = one(None) + + def one(label: Option[String]): Qubit = Qubit(Complex(0), Complex(1), label) - def zero: Qubit = Qubit(Complex(1), Complex(0), None) + def one(label: String): Qubit = one(Some(label)) - def fiftyFifty(label: String): Qubit = this(Complex(1 / Math.sqrt(2.0)), Complex(1 / Math.sqrt(2.0)), Some(label)) + def one: Qubit = one(None) - def fiftyFifty: Qubit = this(Complex(1 / Math.sqrt(2.0)), Complex(1 / Math.sqrt(2.0)), None) + def fiftyFifty(label: Option[String]): Qubit = this(Complex(1 / Math.sqrt(2.0)), Complex(1 / Math.sqrt(2.0)), label) + + def fiftyFifty(label: String): Qubit = fiftyFifty(Some(label)) + + def fiftyFifty: Qubit = fiftyFifty(None) def areAmplitudesValid(q: Qubit): Boolean = MathUtils.isProbabilityValid(Complex.abs(q.a), Complex.abs(q.b)) From 9f973ed91d62405bbe0b3474c61c1074518dc3d7 Mon Sep 17 00:00:00 2001 From: Vasily Vasinov Date: Mon, 30 Sep 2019 12:02:49 -0600 Subject: [PATCH 3/5] Fix qubit init bug --- src/main/scala/scotty/quantum/Qubit.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/scotty/quantum/Qubit.scala b/src/main/scala/scotty/quantum/Qubit.scala index 228f1c2..25cd50b 100644 --- a/src/main/scala/scotty/quantum/Qubit.scala +++ b/src/main/scala/scotty/quantum/Qubit.scala @@ -19,9 +19,9 @@ case class Qubit(a: Complex, b: Complex, label: Option[String]) extends Labeled[ object Qubit { def zero(label: Option[String]): Qubit = Qubit(Complex(1), Complex(0), label) - def zero(label: String): Qubit = one(Some(label)) + def zero(label: String): Qubit = zero(Some(label)) - def zero: Qubit = one(None) + def zero: Qubit = zero(None) def one(label: Option[String]): Qubit = Qubit(Complex(0), Complex(1), label) From c8ddf8f6796ebcce5ab7e67314af943ced7c6063 Mon Sep 17 00:00:00 2001 From: Vasily Vasinov Date: Mon, 30 Sep 2019 12:06:47 -0600 Subject: [PATCH 4/5] Replace overridden `toString` methods with `toHumanString` --- src/main/scala/scotty/quantum/State.scala | 2 +- src/main/scala/scotty/quantum/StateReader.scala | 8 ++++---- src/main/scala/scotty/quantum/gate/Gate.scala | 2 +- src/main/scala/scotty/quantum/math/Complex.scala | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/scala/scotty/quantum/State.scala b/src/main/scala/scotty/quantum/State.scala index 5514ee8..bd4b004 100644 --- a/src/main/scala/scotty/quantum/State.scala +++ b/src/main/scala/scotty/quantum/State.scala @@ -18,7 +18,7 @@ case class Superposition(register: QubitRegister, state: Vector) extends State { if (state.length == 0) sp else ctx.tensorProduct(register, this, sp) - override def toString: String = s"Superposition(${state.toList})" + def toHumanString: String = s"Superposition(${state.toList})" } case class Collapsed(register: QubitRegister, index: Int) extends State { diff --git a/src/main/scala/scotty/quantum/StateReader.scala b/src/main/scala/scotty/quantum/StateReader.scala index 702bd5c..39351ae 100644 --- a/src/main/scala/scotty/quantum/StateReader.scala +++ b/src/main/scala/scotty/quantum/StateReader.scala @@ -21,7 +21,7 @@ case class StateProbabilityReader(state: State)(implicit ctx: QuantumContext) ex case c: Collapsed => Seq(StateData(c.toBinary, Complex(1), 1)) } - override def toString: String = read.flatMap(p => { + def toHumanString: String = read.flatMap(p => { val prob = p.probability.toPercent if (prob == 0) None @@ -55,7 +55,7 @@ case class QubitProbabilityReader(register: Option[QubitRegister], state: State) def read(label: String): Option[QubitData] = read.find(q => q.label.contains(label)) - override def toString: String = read.map(_.toString).mkString("\n") + def toHumanString: String = read.map(_.toString).mkString("\n") } object QubitProbabilityReader { @@ -67,7 +67,7 @@ object QubitProbabilityReader { case class QubitData(label: Option[String], index: Int, probabilityOfOne: Double) { val probabilityOfZero: Double = 1 - probabilityOfOne - override def toString: String = { + def toHumanString: String = { val probZero = probabilityOfZero.toPercent val probOne = probabilityOfOne.toPercent @@ -105,7 +105,7 @@ case class BlochSphereReader(state: State)(implicit ctx: QuantumContext) extends Array(BlochSphereData(phi, theta, Coordinates(x, y, z))) } - override def toString: String = { + def toHumanString: String = { val state = read(0) val phi = Math.toDegrees(state.phi) val theta = Math.toDegrees(state.theta) diff --git a/src/main/scala/scotty/quantum/gate/Gate.scala b/src/main/scala/scotty/quantum/gate/Gate.scala index 70764aa..7e4c0ff 100644 --- a/src/main/scala/scotty/quantum/gate/Gate.scala +++ b/src/main/scala/scotty/quantum/gate/Gate.scala @@ -27,7 +27,7 @@ trait TargetGate extends Gate { def matrix: Matrix = matrixGen.apply(params) - override def toString: String = matrix.toList.map(_.toList.mkString(" ")).mkString("\n") + def toHumanString: String = matrix.toList.map(_.toList.mkString(" ")).mkString("\n") } trait ControlGate extends Gate { diff --git a/src/main/scala/scotty/quantum/math/Complex.scala b/src/main/scala/scotty/quantum/math/Complex.scala index 35c0ad9..acc2c6c 100644 --- a/src/main/scala/scotty/quantum/math/Complex.scala +++ b/src/main/scala/scotty/quantum/math/Complex.scala @@ -3,7 +3,7 @@ package scotty.quantum.math import scotty.quantum.QuantumContext.{Matrix, Vector} case class Complex(r: Float, i: Float) { - override def toString: String = Complex.toString(this) + def toHumanString: String = Complex.toString(this) } object Complex { From d298390cb66768486fc48019901603a8d4162021 Mon Sep 17 00:00:00 2001 From: Vasily Vasinov Date: Mon, 30 Sep 2019 12:47:19 -0600 Subject: [PATCH 5/5] Move `withRegister` init logic to `QubitRegister` --- src/main/scala/scotty/quantum/Circuit.scala | 5 ++--- src/main/scala/scotty/quantum/Register.scala | 8 +++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/scala/scotty/quantum/Circuit.scala b/src/main/scala/scotty/quantum/Circuit.scala index 6a26d86..f203299 100644 --- a/src/main/scala/scotty/quantum/Circuit.scala +++ b/src/main/scala/scotty/quantum/Circuit.scala @@ -10,10 +10,9 @@ case class Circuit(register: QubitRegister, ops: Op*) { def combine(newOps: Op*): Circuit = Circuit(ops ++ newOps: _*) - def withRegister(newRegister: String): Circuit = - Circuit(QubitRegister(newRegister.toCharArray.map(c => Qubit(Bit(c.asDigit))): _*), ops: _*) + def withRegister(newRegister: String): Circuit = Circuit(QubitRegister(newRegister), ops: _*) - def withRegister(newRegister: Int): Circuit = withRegister(newRegister.toBinaryString) + def withRegister(newRegister: Int): Circuit = withRegister(QubitRegister(newRegister)) def withRegister(newRegister: QubitRegister): Circuit = Circuit(newRegister, ops: _*) diff --git a/src/main/scala/scotty/quantum/Register.scala b/src/main/scala/scotty/quantum/Register.scala index 1778a1e..4e7533b 100644 --- a/src/main/scala/scotty/quantum/Register.scala +++ b/src/main/scala/scotty/quantum/Register.scala @@ -16,6 +16,12 @@ sealed trait Register[T <: Labeled[String]] { require(areLabelsUnique, ErrorMessage.RegisterLabelsNotUnique) } +case class BitRegister(values: Bit*) extends Register[Bit] + case class QubitRegister(values: Qubit*) extends Register[Qubit] -case class BitRegister(values: Bit*) extends Register[Bit] \ No newline at end of file +object QubitRegister { + def apply(r: String): QubitRegister = this(r.toCharArray.map(c => Qubit(Bit(c.asDigit))): _*) + + def apply(r: Int): QubitRegister = this(r.toBinaryString) +} \ No newline at end of file