Scotty 0.6.0 Released
Performance Improvements
This is a very important update that focuses on the simulator performance. In addition to the state vector memory allocation optimizations we rewrote the gate application algorithm from scratch. The new algorithm is three orders of magnitude faster than what we had before. In practical terms, it means that users can run much larger circuits in a fraction of the time required before. For example, a 15-qubit circuit with 1,000 gates now runs in under one second on a 4.2 GHz Intel Core i7 box. A 20-qubit circuit with 1,000 gates runs in about 20 seconds. Users can realistically run shallower circuits of up to 27 qubits with the max of 29 qubits.
In order to optimize for memory usage, we replaced the double-precision (Double
) complex number representation with regular Float
s. This cut our memory usage in half. Now, every complex number takes up 64 bits instead of 128. It means that Vector
and Matrix
types were redefined as:
type Vector = Array[Float]
type Matrix = Array[Array[Float]]
Multi-Trial Experiment Changes
The runAndMeasure
version of the method that ran multiple trials of the same circuit was renamed to runExperiment
to avoid confusion with the single shot runAndMeasure
.
We also removed parallelism support for multiple trials since it doesn’t make sense to run experiments in parallel given that state initialization, gate application, and probability calculations are already parallelized.
Gate Changes
Single-qubit gate implementations now live at the framework level. Some are implemented with matrices and some are expressed through other gates.
CircuitConnector
was removed in favor of CompositeGate
. You can define CompositeGate
s to describe multi-qubit unitary gates and operations. Some gates in the Scotty simulator now use this technique. For example, the SWAP
gate is implemented as a function that takes custom indexes as two parameters and returns a CompositeGate
:
def SWAP(i0: Int, i1: Int) = CompositeGate(CNOT(i0, i1), CNOT(i1, i0), CNOT(i0, i1))
Performance improvements forced us to introduce limitations to how Dagger
and Controlled
modifiers work. Now, Dagger
can only be applied to single-qubit gates and Controlled
can either be applied to other Controlled
gates or single-qubit gates. This behavior might change in the future.
Register Changes
QubitRegister
can now be initialized from a binary string or an integer:
QubitRegister("1001")
// is equivalent to
QubitRegister(9)
Circuit
also received a couple of helper methods for quick register initialization. Here are all the different ways you can set a custom register:
Circuit(CNOT(0, 2)).withRegister(QubitRegister(Qubit.one, Qubit.zero, Qubit.zero))
Circuit(CNOT(0, 2)).withRegister(Qubit.one, Qubit.zero, Qubit.zero)
Circuit(CNOT(0, 2)).withRegister("100")
Circuit(CNOT(0, 2)).withRegister(4)