Skip to content

Commit

Permalink
Merge pull request #49 from vasinov/master
Browse files Browse the repository at this point in the history
Refactor bit, qubit and qubit register, rename `GateGroup` to `CompositeGate`
  • Loading branch information
vasinov authored Sep 30, 2019
2 parents 3da5a12 + d298390 commit fc6d453
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 40 deletions.
12 changes: 7 additions & 5 deletions src/main/scala/scotty/quantum/Bit.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package scotty.quantum

import scotty.quantum.QuantumContext.Vector
import scotty.quantum.math.Complex
import scotty.{ErrorMessage, Labeled}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 4 additions & 5 deletions src/main/scala/scotty/quantum/Circuit.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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: _*)

Expand All @@ -22,7 +21,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

Expand Down
20 changes: 13 additions & 7 deletions src/main/scala/scotty/quantum/Qubit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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})"

Expand All @@ -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 = zero(Some(label))

def zero(label: String): Qubit = Qubit(Complex(1), Complex(0), Some(label))
def zero: Qubit = zero(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))

Expand Down
8 changes: 7 additions & 1 deletion src/main/scala/scotty/quantum/Register.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
object QubitRegister {
def apply(r: String): QubitRegister = this(r.toCharArray.map(c => Qubit(Bit(c.asDigit))): _*)

def apply(r: Int): QubitRegister = this(r.toBinaryString)
}
2 changes: 1 addition & 1 deletion src/main/scala/scotty/quantum/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/scotty/quantum/StateReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/scotty/quantum/gate/Gate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/scotty/quantum/math/Complex.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
26 changes: 13 additions & 13 deletions src/main/scala/scotty/simulator/QuantumSimulator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down Expand Up @@ -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())

Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/scotty/simulator/DefGateSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit fc6d453

Please sign in to comment.