Circuits and Qubits


Let’s take a look at the core abstractions that, for the most part, serve as immutable containers for various quantum primitives. The top-level primitive is called Circuit. Each circuit can be thought of as a container for one QubitRegister—a group of qubits (Qubit)—and a sequence of quantum operations (Op).

Circuits can be combined sequentially:

val newCircuit = Circuit(H(0)).combine(Circuit(CNOT(0, 1))

All circuits come with an implicit qubit register where all qubits are initialized at $\lvert0\rangle$. The number of qubits is based on the highest operation index. For example, Circuit(H(5)) will be initialized with a register of six qubits (index starts at zero). You can always specify a custom register by applying withRegister to your circuit:

Circuit(CNOT(0, 2)).withRegister(Qubit.one, Qubit.zero, Qubit.zero)

Qubits are immutable containers with two Complex numbers describing their quantum state:

$\alpha$ and $\beta$ match Qubit a and b parameters. Since these parameters represent qubit probabilities their squares have to add up to one:

All qubits in the register have to be accessed by their index in the underlying sequence. For easy access to the actual numerical indexes Circuit has a helper property indexes.

Qubits implement the Labeled trait, which means that you can apply a unique string label to any qubit:

val testQubit = Qubit.one("test")

val messageQubit = Qubit(Complex(0.8), Complex(0.6), "message")

Labels are useful for when you need to quickly differentiate between qubits and when you look at the final classical register readouts after the measurement.