_Quantum programming with dann5

Taking advantage of 5K annealing qubits in D-Wave Advantage, requires tedious programming using Python APIs, which resembles assembler level programming on classical computers.

To better understand challenges, let’s use the ‘friend & enemies’ problem that you can find within D-Wave training material. The problem requires us to group Xavier, Yolanda, Zeke and Wanda. If we know that Xavier and Yolanda are friends, Yolanda and Zeke are enemies, while Zeke and Wanda are also friends. To solve the problem, if we were to use D-Wave’s Advantage2 with Zephyr topology, or any of the previous version of quantum annealer, we are to write following QUBO formula, i.e., one of potential quantum annealer programs:

QUBO = −2+𝑥+𝑤−2𝑥𝑦+2𝑦𝑧−2𝑧𝑤

Once we run it, the quantum solver will return the two expected solutions

  • x (for Xavier) and y are 0, while z and w are 1, or
  • x and y are 1, while x and w are 0.

As much as the QUBO formula is simple, without thoughtful analysis it is not obvious how we have arrived at the above formula as an accurate description of the problem.

dann5 · PyPI quantum programming platform provides comfort of 3rd-generation general-quantum programming in Python through use of quantum types, such as Qbit, Qbool, Qbin, Qwhole and Qint, and appropriate bitwise, logical, comparison or arithmetic operations. To solve the problem of ‘friends & enemies’, logically we can state the following:

  1. We don’t know what value(s) x, y, z and w should take, i.e. they are unknown to our quantum program, or in quantum world they are in superposition
  2. x is the same as y, and y is different than z, and z is the same as w

To solve the problem, we write the following Python code using dann5’s d5o library and Qbit as a data type for our variables:

x = d5o.Qbit("x"); y = d5o.Qbit("y"); z = d5o.Qbit("z"); w = d5o.Qbit("w");
_1 = d5o.Qbit("_1",1)
expr = _1 == ( ~(x ^ y) & (y ^ z) & ~(z ^ w) )

The first line of code corresponds to the previous statement (1). We have initialized Python variables x, y, z, and w, as Qbit-s with the same names in quantum space and undefined values (i.e., in superposition states). Additionally, in line 2, we have initialized variable _1 as Qbit with a value 1.

The third line of the program corresponds to the statement (2) above. Knowing that the bitwise logic (y ^ z), or (y XOR z) is same as (y is unlike z), and that inverted XOR logic like ~(x ^ y) means (x is alike y), we have stated that the statement (x alike y) and (y unlike z) and (z alike w) must be correct, i.e. the expression has to be equal to 1.

Now, if we request the expression’s QUBO and print it:

print( expr.qubo() )

we will see that the generated QUBO has 30+ leaner and quadratic elements. However, when solved on a quantum annealer:

print( expr.solve() )

it will provide same solutions as the smaller QUBO. So, for any problem there is a set of equivalent QUBO formulas, which describe the problem.

The advantage of 3rd-generation quantum-programming over quantum-assembler-programming becomes more obvious if we expand the ‘friends & enemies’ problem by adding Nancy and Orlando as friends, and Paul and Sean as friends, but with consideration that Orlando, Paul, Yolanda, and Zeke are enemies.

To solve this problem in Python, we initialize the variables as Qbin-s (quantum binary-s) with 2 Qbit-s, as in the first 2 lanes of the code below. The initialized variables are unknown, i.e. their Qbit-s are in superposition state, while variables _1 and _3 are determined, with values 1 and 3 respectively.

import dann5.d5o2 as d5o

x = d5o.Qbin(2, "x"); y = d5o.Qbin(2, "y"); z = d5o.Qbin(2, "z"); w = d5o.Qbin(2, "w"); 
n = d5o.Qbin(2, "n"); o = d5o.Qbin(2, "o"); p = d5o.Qbin(2, "p"); s = d5o.Qbin(2, "s"); 
_1 = d5o.Qbit("_1", 1); _3 = d5o.Qbin("_3", d5o.Bits(0b11))

expr1 = _3 == ( ~(x ^ y) & (y ^ z) & ~(z ^ w) )
print(expr1)
expr2 = _3 == ( ~(n ^ o) & (o ^ p) & ~(p ^ s) )
print(expr2)
expr3 = _1 == ( ( s[1] ^ y[1] ) | ( s[0] ^ y[0] ) )
print(expr3)
expr4 = _1 == ( ( s[1] ^ z[1] ) | ( s[0] ^ z[0] ) )
print(expr4)

eFnE = d5o.Qblock() << expr1 << expr2 << expr3 << expr4
print(eFnE)
qubo = eFnE.qubo()
print(qubo)

from dwave.system import DWaveSampler, EmbeddingComposite
sampler = DWaveSampler(solver={'topology__type': 'zephyr', 'qpu': True})
solver = EmbeddingComposite(sampler)
kwargs = {'num_reads': 1000 }

sampleset = solver.sample_qubo(qubo, **kwargs)
lowEnergy = sampleset.lowest().record['energy'][0]
samples = [d5o.SampleEng(dict(sample), lowEnergy) for sample in sampleset.lowest().samples()]
eFnE.add(samples)
print("  {}".format(eFnE.solutions()))
varBind = d5o.Qbinder() << x << y << z << w << n << o << p << s
varBind.add(samples)
print(varBind)

Again, we state in expression (1) that (x alike y) & (y unlike z) & (z alike w) must be correct. Similarly, the expression (2) states that (n alike o) & (o unlike p) & (p alike s) must be correct for both Qbit-s. To ensure that Sean and Yolanda are enemies, it is enough for one of their Qbit-s to be different, as in expression (3), (s[1] unlike y[1]) or (s[0] unlike y[0]). Similarly, to ensure Sean and Zeke are enemies we are adding the expression (4) forcing at least one of their Qbit-s to be different.

To calculate QUBO for the ‘expanded friend & enemies’ program, we enclose expressions (1), (2), (3), and (4) into a Qblock, which will provide combined QUBO function for the problem. Interestingly, the generated QUBO in a binary-quadratic-model (BQM) has 52 linear and 114 quadratic elements. The linear elements correspond to logical quantum nodes, which are mapped to physical quantum annealer qubits. The quadratic elements correspond to graph branches connecting logical quantum nodes, which in Zephyr topology will mostly be mapped to couplers between physical quantum qubits. If we know all of this, we can assume with high certainty that, when released, Advantage2, with 7,000 physical annealing qubits, will support 135 times more complex dann5 quantum program than the “expanded friend & enemies” example.

By adding calculated samples into eFnE Qblock it is possible to print eFnE.solutions(), which will show the result for all intended and augmented variables. For easier review of the results, it is better to use Qbinder, e.g. varBind, to add calculated samples into just intended variable. By inserting x, y, z, w, n, o, p and s variables into a Qbinder, the results will be displayed as in example below

x\2b:U\ y\2b:U\ z\2b:U\ w\2b:U\ n\2b:U\ o\2b:U\ p\2b:U\ s\2b:U\ {
x\2:10\ y\2:10\ z\2:01\ w\2:01\ n\2:11\ o\2:11\ p\2:00\ s\2:00\ 
x\2:10\ y\2:10\ z\2:01\ w\2:01\ n\2:00\ o\2:00\ p\2:11\ s\2:11\ 
x\2:00\ y\2:00\ z\2:11\ w\2:11\ n\2:10\ o\2:10\ p\2:01\ s\2:01\
}

Python dann5 library d5o provide intuitive comfort of programming like with any 3rd-generation programming language allowing existing programmers to experiment and pilot programs that exploit hybrid-power of classical, machine-learning and quantum programming. The beta version of d5o library for programming on D-Wave quantum annealers is released under GNU public license (GPLv3) and you can visit dann5 · PyPI, for instructions on how to install and use the library. 

 

 

2

Comments

0 comments

Please sign in to leave a comment.

Didn't find what you were looking for?

New post