Skip to main content

Qbit

At a glance

  • Qbit represents a spin-12\frac{1}{2} (or qubit) product state in the computational basis m=m0mL1|m\rangle = |m_0\rangle \otimes \cdots \otimes |m_{L-1} \rangle with support on LL sites and mj{0,1}m_j \in \{0,1\}.
  • Qbit provides various ways to manipulate product states with methods that transform the product state.
  • Qbit supports interactions with non-branching operators and can be used as an index for quantum states.
  • Qbit is designed to be the workhorse of exact and Monte Carlo type methods that need to efficiently manipulate computational basis states.

Overview of class

Qbit is a class that has three components: a bitstring represented by an integer, the number of degrees of freedom and a coefficient represented by a complex number. Together these represent the computational basis states for a spin 12\frac{1}{2} system. To see this consider a general many body state with L degrees of freedom,

ψ=m=02L1cmm.| \psi \rangle = \sum_{m=0}^{2^L-1} c_m |m\rangle.

where cmCc_m \in \mathbb{C} and m|m\rangle can be broken down into its individual degrees of freedom

m=m0m1mL1|m\rangle = |m_0\rangle \otimes |m_1\rangle \otimes \cdots |m_{L-1} \rangle

Where mj{0,1}m_j \in \{0,1\} or alternatively mj{,}m_j \in \{\uparrow, \downarrow\} and LL is the total number of degrees of freedom. A Qbit is simply a single term in the summation describing ψ|\psi\rangle, namely cmc_m and m|m\rangle.

Using Qbit

Qbit is a datatype designed for exact and Monte Carlo workloads, allowing you to express and modify states in the computational basis with ease, and interact with the rest of the quantum toolkit.

Create a Qbit

Qbit objects can be initialized to some configuration using strings of 0s and 1s. If you do not specify the coefficient the Qbit will have a coefficient of c=1c=1. Alternative to specifying the state with a string, you can specify the number of degrees of freedom as an integer with an optional coefficient, initializing the state to the all 0 state.

var qa = Qbit("010101")     //   (1.0+0i)|010101>
var qb = Qbit("010101",2.0) // (2.0+0i)|010101>
var qc = Qbit(6) // (1.0+0i)|000000>
var qd = Qbit(6,-1.5+1.0i) // (-1.5+1i)|000000>

Accessing and setting properties of Qbit

The three components of Qbit can be fetched or set.

var qa = Qbit("0101")     //   (1.0+0i)|0101>

// Number of degrees of freedom
print(qa.num_sites()) // -> 4

// you can likewise set the number of sites
qa.set_num_sites(5);
print(qa.num_sites()) // -> 5

// accessing and modifying the coefficient
print(qa.coefficient) // -> 1+0i
qa.coefficient = 2
print(qa.coefficient) // 2+0i

// setting and getting the underlying integer
print(integer(qa)) // -> 10
qa.set_representation(2)
print(qa) // -> (2+0i)|01000>

// You can re-initialize the state with a different bit string, length
//and coefficient by calling the constructor.
qa = Qbit("000") // -> (1+0i)|000>
tip

To efficiently update the bit string of a Qbit you can use binary notation.

var q = Qbit(4); // -> (1+0i)|0000>
q.set_representation(0B0010) // -> |0100>
// note this is equivalent to initializing q with the string "0100"
warning

Binary notation is written from right to left and returns an integer. When we write our state with a string of 0s and 1s we read the state from left to right. These conventions are flipped deliberately to allow users to think about chains of spin 12\frac{1}{2} degrees of freedom as being indexed from left to right. Care must be taken to account for this convention difference when initializing the integer representation of a Qbit with binary notation.

note

Because of the binary notation convention used above, the computational basis differs from standard literature. For example, the traditional computational basis state with integer representation 11 is 01|01\rangle because binary bit strings are read from right to left traditionally. However, in our convention we have that 01|01\rangle is 22 since we read from left to right. While care is needed in transforming between the two bases, staying in one convention guarantees correctness.

Modifying Qbit with bit operations

The bit string representation of Qbit may be changed with a number of methods that either modify the Qbit in place or return a transformed copy. Functions with present tense verbs act in-place and return a reference to the Qbit state. To get a copy without modifying the original, use the past tense version of the same function.

var qa = Qbit("1000")      // (1.0+0i)*|1000>

// translating the bits to the right
qa.translate(1) // (1+0i)*|0100>
var qb = qa.translated() // (1+0i)*|0010>, qa unchanged

// more than 1 translation can be done a time
qa.translate(2) // (1+0i)*|0001>

Other global bit modifications include flip and reflect which flip all bits and reflect the bit string about the center. All of these transformations assume a linear chain of degrees of freedom. Qbit can be used in conjunction with Lattice to transform the state with respect to other geometries.

You can also simply flip target bits,

var q = Qbit(4);
q.flip(0); // -> (1+0i)|1000>

Along with global transformations you can directly access and set the bits.

var q = Qbit("0000")
// set the state in the 0th site to 1.
q.set_state(0,1);
print(q.get_state(0)) // -> 1
tip

When getting and setting the bits of your state you are giving and receiving integers not strings.

You can also split a Qbit in two if you are working with partitions of your state.

var q = Qbit("1001");
// need to specify the size of the left and right subsystem
var q_pair = q.split(2,2);
print(q_pair[0]) // -> (1+0i)|10>
print(q_pair[1]) // -> (1+0i)|01>

You can also split a Qbit by specify the indices to form your bipartition.

var q_pair = Qbit("001101", 2.0 + 1.0i).split([0,2,3],[1,4,5]) 
// -> [(2 + 1i)|011>, (1 + 0i)|001>]
tip

When you split a Qbit into two, the first Qbit returned will absorb the coefficient of the original you are splitting, the second Qbit will have a coefficient of unity.

We can also combine two Qbit objects by taking a Kronecker product

var q0 = Qbit("10")
var q1 = Qbit("01",2.0)
var q01 = q0.kron(q1) // -> (2+0i)|1001>

This code is equivalent to the mathematical operation,

10201=21001.|10\rangle \otimes 2|01\rangle = 2|1001\rangle.

It is sometimes convenient to insert a new degree of freedom or bit into a state to construct a state you are targeting. This can be accomplished by inserting a zero and growing the bit string.

var q = Qbit("1111")
q.insert(1) // (1+0i)|10111>

Indexing vectors and matrices with Qbit

Since Qbit is intended to represent a basis state, it can be used to index vectors and matrices like an integer.

var q = Qbit("0000")
var vec = zeros([2**q.num_sites], as_complex | as_matrix)
// the all zero state we declared with psi corresponds to the integer 0
vec[0] = 1
print(vec[q])

// you can likewise pick out matrix entries with psi
var M = random([4,4], as_complex | as_matrix)
var a = Qbit("10")
var b = Qbit("01")
print(M[a,b])
warning

These operations disregard the coefficient of the Qbit. Picking out a matrix element treats the Qbit as an integer and is only equivalent to aMb\langle a| M |b\rangle if the Qbit objects have unit coefficients.

Looping over Qbits

It is often useful to loop over all possible computational basis states for a fixed number of degrees of freedom, this can be accomplished easily with a state_range

// here we specify as_spinhalf and as_productstate which specifies a Qbit. 
// 4 is the number of degrees of freedom.
for(psi : state_range(as_spinhalf | as_productstate, 4))
{
print(psi);
}

Best practices

  • String initialization is provided for convenience and should only be used for clarity and initialization. Otherwise it is preferable to always interact with Qbit through bit and integer operations.
  • Qbit objects are not a replacement for full dense or sparse representations of quantum states. Instead they are provided to help you navigate between computational basis states. This means if you are writing an algorithm that requires 10s of Qbit objects, there is likely a more efficient way to do it. Stick to implementations that require small numbers of Qbit objects that navigate a larger space as you iterate.

Detailed documentation

To check out more detailed documentation see Qbit in the Library Reference.

Documentation Contributors

Jonathon Riddell

Sebastien J. Avakian