Quantum Context and Simulator
Quantum Context
First and foremost, Scotty is a high-level quantum framework that defines abstract quantum primitives. At the top of it all we have QuantumContext
—a trait with a handful of abstract methods. This is the heart of the framework and it’s meant to provide functions for quantum state and gate transitions that take place during circuit execution.
Here is a list of method signatures that QuantumContext
provides:
trait QuantumContext {
def tensorProduct(register: QubitRegister,
sp1: Superposition, sp2: Superposition): Superposition
def densityMatrix(vector: Vector): Matrix
def isUnitary(gate: TargetGate): Boolean
def probabilities(sp: Superposition): Seq[StateData]
def applyGate(state: Vector, gate: Gate): Unit
def run(circuit: Circuit): State
def measure(register: QubitRegister, state: Vector): Collapsed
def runAndMeasure(circuit: Circuit): Collapsed
def runExperiment(circuit: Circuit, trialsCount: Int): ExperimentResult
}
Vector
and Matrix
are defined in the following way:
type Vector = Array[Float]
type Matrix = Array[Array[Float]]
The framework makes no assumptions about the underlying state implementation. The only thing that’s expected is that Vector
and Matrix
arrays represent lists of complex numbers, each occupying two elements in the array for their real and imaginary components. There’s no built-in data type for complex numbers in Java, so we have to either represent them with objects (which is hugely inefficient for large arrays in most cases) or with two separate primitive elements. Scotty uses two floats to represent a complex number.
Java floats take up 32 bits each, so a complex number in Scotty takes up 64 bits. Since the state vector uses a Java array there’s a hard limit of Integer.MAX_VALUE
elements that it can have, which corresponds to elements in our case. It means that Scotty can represent states of up to 29 qubits.
There are many possible use cases where you’d want to roll out your own implementation of QuantumContext
. For example, if you have a distributed quantum simulator running in the cloud that you want to use to run bigger circuits then implementing QuantumContext
would make a lot of sense. The obvious benefit is that you get to keep your Scala programs and algorithms written with Scotty unmodified. Write once, run anywhere.
Quantum Simulator
QuantumSimulator
is a built-in implementation of the QuantumContext
, which allows you to run up-to-29 qubit quantum circuit simulations. All simulation computations are (depending on your hardware) parallelizable on the CPU and you can control the underlying thread pool implementation by providing a custom execution context:
val ec = ExecutionContext.fromExecutor(
new java.util.concurrent.ForkJoinPool(8)
)
QuantumSimulator(ec)
QuantumSimulator
can also take a Random
instance. It’s useful when you need to specify a random seed:
QuantumSimulator(new Random(System.nanoTime))
The simulator has a runExperiment
method that runs multiple experiment trials. You can pass the number of trials to it and it will run them sequentially.
After all trials are done executing, the method returns an ExperimentResult
which contains a list of Collapsed
states after each experiment.
The stateStats
lazy variable is a list of tuples containing a binary representation of the collapsed state and the total number of state occurrences. Suppose, we entangle two qubits and run the experiment 1000 times:
QuantumSimulator()
.runExperiment(Circuit(H(0), CNOT(0, 1)), 1000)
.stateStats.toHumanString
As expected, we are going to end up with roughly 50% of both qubits being in the 00
state and another 50% being in the 11
state:
00: 483
01: 0
10: 0
11: 517
- Getting Started
- Quantum Context and Simulator
- Circuits and Qubits
- Superposition and Measurement
- Operations
- Standard Gates
- Modifiers and Custom Gates
- State Readers