Uncategorized

Quantum Arithmetic – Adder, Multiplier, and Exponentiation (Q#)

(Download source code from here.)

In previous post, I have explained about the brief outline of Quantum Fourier Transform (QFT).
In this post, I’ll show you several primary arithmetic implementation in quantum circuits – such as quantum addition, subtraction, multiplication, and exponentiation – using Quantum Fourier Transform (QFT).

This idea will lead to the next post, famous Shor’s algorithm’s implementation.

Series : Quantum algorithm’s implementation (Q#)

In today’s technologies, available qubits in quantum utility is so limited. Thus there are several researches to reduce qubits or improve performance for the implementation. (See “Circuit for Shor’s algorithm using 2n + 3 qubits” or “Fast Quantum Modular Exponentiation Architecture” for instance.)
But, for the purpose of your understanding, I’ll implement algorithm very straightforward without any of these optimization in this post.

Quantum Adder

Adder operation for each bits (such as, 1 + 1) is trivial on the classical computing. But how to implement same operation on quantum computing ?
Here, I’ll show you Draper’s algorithm which is quantum addition operation based on QFT.

Note : When there exist 3 qubits, the quantum addition makes such as :
5 (101) + 4 (100) = 1 (001) mod 8

As you saw in my previous post, Quantum Fourier Transform (QFT) can be used to translate a bit shift into phase shifts.
The outline idea of Draper’s algorithm is :

  1. Translate into phase shifts with QFT
  2. Implement modular basis (cyclic) addition on phase
  3. The state is then reversed by QFT^{-1} , after addition.

The following diagram is the circuit for Draper adder for

\displaystyle \left| x \right> \left| y \right> \to \left| x \right> \left| x+y \: mod \: 2^n \right>

where \left| x \right> = \left| x_0 \right> \left| x_1 \right> \ldots \left| x_{n-1} \right> and \left| y \right> = \left| y_0 \right> \left| y_1 \right> \ldots \left| y_{n-1} \right> .

Here I don’t explain why this circuit realizes addition, but please see “Quantum arithmetic with the Quantum Fourier Transform” for details.

where \displaystyle R_k = \begin{pmatrix} 1 & 0 \\ 0 & e^{2 \pi i / 2^k} \end{pmatrix}

Figure : Draper Adder \left| x \right> \left| y \right> \to \left| x \right> \left| x+y \: mod \: 2^n \right>

The following is the implementation for this circuit by Q#.
QFTImpl() is an QFT operation which is implemented in my previous post, but you can instead use built-in operation Microsoft.Quantum.Canon.QFT() in Q# library.

Q# Operations

open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;

//
// Implement : |x⟩ |y⟩ -> |x⟩ |x + y mod 2^n⟩ where n = Length(x) = Length(y)
// with Drapper algorithm (See https://arxiv.org/pdf/1411.5949.pdf)
//
operation QuantumAdd (x : Qubit[], y : Qubit[]) : Unit is Adj + Ctl {
  let n = Length(x);
  QFTImpl(y);
  for i in 0 .. n - 1 {
    for j in 0 .. (n - 1) - i {
      Controlled R1Frac([x[i + j]], (2, j + 1, (y)[(n - 1) - i]));
    }
  }
  Adjoint QFTImpl(y);
}

operation TestQuantumAdd (a : Int, b : Int, n : Int) : Int {
  mutable resultArray = new Result[n];

  use (x, y) = (Qubit[n], Qubit[n]) {
    // create qubit's array from integer a (ex: 3 -> |011⟩)
    mutable array1 = new Bool[n];
    mutable tempInt1 = a;
    for idxBit in 0 .. n - 1 {
      set array1 w/= ((n - 1) - idxBit) <- tempInt1 % 2 == 0 ? false | true;
      set tempInt1 = tempInt1 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array1[idx]) {
        X(x[idx]);
      }
    }

    // create qubit's array from integer b (ex: 3 -> |011⟩)
    mutable array2 = new Bool[n];
    mutable tempInt2 = b;
    for idxBit in 0 .. n - 1 {
      set array2 w/= ((n - 1) - idxBit) <- tempInt2 % 2 == 0 ? false | true;
      set tempInt2 = tempInt2 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array2[idx]) {
        X(y[idx]);
      }
    }

    // apply Drapper adder
    QuantumAdd(x, y);

    // measure and reset
    for idx in 0 .. n - 1 {
      set resultArray w/= idx <- MResetZ(y[idx]);
    }
    for idx in 0 .. n - 1 {
      Reset(x[idx]);
    }
  }

  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = Microsoft.Quantum.Convert.ResultArrayAsBoolArray(resultArray);
  mutable resultInt = 0;
  for idx in 0 .. n - 1 {
    if(resultBool[idx]) {
      set resultInt = resultInt + (2 ^ ((n - 1) - idx));
    }
  }
  return resultInt;
}

Invoking from Python (Use Python or .NET)

n = 3
b = 3
for a in range(3, 8):
  res = TestQuantumAdd.simulate(a=a, b=b, n=n)
  print("{} + {} is {} (mod {})".format(a, b, res, 2**n))
n = 3
a = 7
for b in range(4, 8):
  res = TestQuantumAdd.simulate(a=a, b=b, n=n)
  print("{} + {} is {} (mod {})".format(a, b, res, 2**n))

When you want to subtract, you just run this operation in reverse (i.e, QuantumAdd^{-1} ) as follows.
The following operation results into y - x . (for example, 5 – 7 = 6 mod 8)

// Implement : |x⟩ |y⟩ -> |x⟩ |y - x⟩
Adjoint QuantumAdd(x, y);

You can also implement \left| x \right> \to \left| x+a \: mod \: 2^n \right> for a given classical numeric integer a , by running classical logical operations instead of quantum controlled gates.
See the following sample code. (This code uses logical conditions instead of using controlled gates, and other parts are same as previous code.)

Q# Operations

//
// Implement : |x⟩ -> |x + b mod 2^n⟩ for some integer b
//
operation QuantumAddByNumber (x : Qubit[], b : Int) : Unit is Adj + Ctl {
  let n = Length(x);

  // apply Draper adder for numeric
  QFTImpl(x);
  for i in 0 .. n - 1 {
    for j in 0 .. (n - 1) - i {
      if(not((b / 2^((n - 1) - (i + j))) % 2 == 0)) {
        R1Frac(2, j + 1, (x)[(n - 1) - i]);
      }
    }
  }
  Adjoint QFTImpl(x);
}

operation TestQuantumAddByNumber (a : Int, b : Int, n : Int) : Int {
  mutable resultArray = new Result[n];

  use x = Qubit[n] {
    // create qubit's array from integer a (ex: 3 -> |011⟩)
    mutable array1 = new Bool[n];
    mutable tempInt1 = a;
    for idxBit in 0 .. n - 1 {
      set array1 w/= ((n - 1) - idxBit) <- tempInt1 % 2 == 0 ? false | true;
      set tempInt1 = tempInt1 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array1[idx]) {
        X(x[idx]);
      }
    }

    // apply Draper adder for numeric
    QuantumAddByNumber(x, b);

    // measure and reset
    for idx in 0 .. n - 1 {
      set resultArray w/= idx <- MResetZ(x[idx]);
    }
  }

  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = Microsoft.Quantum.Convert.ResultArrayAsBoolArray(resultArray);
  mutable resultInt = 0;
  for idx in 0 .. n - 1 {
    if(resultBool[idx]) {
      set resultInt = resultInt + (2 ^ ((n - 1) - idx));
    }
  }
  return resultInt;
}

Invoking from Python (Use Python or .NET)

n = 3
b = 3
for a in range(3, 8):
  res = TestQuantumAddByNumber.simulate(a=a, b=b, n=n)
  print("{} + {} is {} (mod {})".format(a, b, res, 2**n))
n = 3
a = 7
for b in range(4, 8):
  res = TestQuantumAddByNumber.simulate(a=a, b=b, n=n)
  print("{} + {} is {} (mod {})".format(a, b, res, 2**n))

Quantum Multiplier

Once you’ve completed quantum addition, it’s easy to implement multiplier : \left| y \right> \to \left| a \cdot y \: mod \: 2^n \right> for some integer a .
For doing this, just repeat the previous addition “a ” times.

See the following Q# code.
In this code, previous QuantumAdd() is repeated by for() loop statement.

However, it’s not completed.
As you can see below, this operation needs ancilla bits \left| s \right> to store the intermediate results. And our concern is how to release these ancilla qubits.
Of course, you can simply use Reset() operation for clean-up. But if you want to implement this operation as a unitary operation, you must keep reversibility and you cannot then use Reset() operation. (If you don’t intend to implement as a controllable unitary operation, you can use Reset() operation.)
Hence, in order to make ancilla qubits \left| s \right> to \left| 0 \right> state, here I use the following identity :

a \cdot z = 1\:mod\:N,\:y^{\prime} = a \cdot y\:mod\:N \Rightarrow y = z \cdot y^{\prime} \:mod\:N

For implementing this clean-up operation, an integer (a ) and modulus (2^n ) should be co-prime. (i.e, It’s equivalent that an integer “a ” must be odd in this case.)

Q# Operations

//
// Implement : |y⟩ -> |a y mod 2^n⟩ for some integer a
//
// Important Note :
// Integer "a" and modulus 2^n must be co-prime number.
// (For making this operator must be controlled. Otherwise InverseModI() raises an error.)
//
operation QuantumMultiply (a : Int, y : Qubit[]) : Unit is Adj + Ctl {
  let n = Length(y);
  let a_mod = a % (2^n);
  use s = Qubit[n] {
    // start |y⟩ |0⟩

    // apply adder by repeating "a" (integer) times
    for r in 0 .. a_mod - 1 {
      QuantumAdd(y, s);
    }
    // now |y⟩ |a y mod N⟩

    // swap first register and second one by tuple
    Microsoft.Quantum.Canon.ApplyToEachCA(SWAP, Microsoft.Quantum.Arrays.Zipped(y, s));
    // now |a y mod N⟩ |y⟩

    // reset all s qubits !
    // but it's tricky because we cannot use "Reset()". (See my above description.)
    let a_inv = InverseModI(a_mod, 2^n);
    for r in 0 .. a_inv - 1 {
      Adjoint QuantumAdd(y, s);
    }
  }
}

operation TestQuantumMultiply (a : Int, b : Int, n : Int) : Int {
  mutable resultArray = new Result[n];

  use y = Qubit[n] {
    // create qubit's array from integer b (ex: 3 -> |011⟩)
    mutable array2 = new Bool[n];
    mutable tempInt2 = b;
    for idxBit in 0 .. n - 1 {
      set array2 w/= ((n - 1) - idxBit) <- tempInt2 % 2 == 0 ? false | true;
      set tempInt2 = tempInt2 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array2[idx]) {
        X(y[idx]);
      }
    }

    // apply multiplier
    QuantumMultiply(a, y);

    // measure and reset
    for idx in 0 .. n - 1 {
      set resultArray w/= idx <- MResetZ(y[idx]);
    }
  }

  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = Microsoft.Quantum.Convert.ResultArrayAsBoolArray(resultArray);
  mutable resultInt = 0;
  for idx in 0 .. n - 1 {
    if(resultBool[idx]) {
      set resultInt = resultInt + (2 ^ ((n - 1) - idx));
    }
  }
  return resultInt;
}

Invoking from Python (Use Python or .NET)

n = 3
a = 5
for b in range(3, 8):
  res = TestQuantumMultiply.simulate(a=a, b=b, n=n)
  print("{} x {} is {} (mod {})".format(a, b, res, 2**n))

Quantum Exponentiation

Next we implement modular exponentiation : \left| x \right> \to \left| a^x \: mod \: N \right> (N = 2^n ) for some integer a .

As you can easily see, this exponentiation inside ket notation can be decomposed as follows.

a^x \: mod \: N = (a^{2^{n-1}} \: mod \: N)^{x_0} (a^{2^{n-2}} \: mod \: N)^{x_1} \cdots (a^{2^0} \: mod \: N)^{x_{n-1}} \: mod \: N

where \left| x \right> = \left| x_0 \right> \left| x_1 \right> \ldots \left| x_{n-1} \right> , \; x_k \in \{0, 1\}

Hence, using quantum multiplier U_m : \left| s \right> \to \left| m \cdot s \right> and controlled gates, this can be implemented by the following circuit.

Figure : Exponentiation using Unitary Multiplier (U)

The following is Q# implementation for this circuit. (In this example, we simply use Reset() operation.)
In this operation, you should also provide co-prime integer “a ” as input variable. (Because this uses previous multiplier.)

Q# Operations

//
// Implement : |x⟩ -> |a^x mod 2^n⟩ for some integer a
//
// Important Note :
// Integer "a" and modulus 2^n must be co-prime number.
// (Because this invokes QuantumMultiply().)
//
operation QuantumExponent (a : Int, x : Qubit[]) : Unit {
  let n = Length(x);
  use s = Qubit[n] {
    // set |s⟩ = |1⟩
    X(s[n - 1]);

    // apply decomposition elements
    for idx in 0 .. n - 1 {
      Controlled QuantumMultiply([x[idx]], (a^(2^((n-1) - idx)), s));
    }

    // swap |x⟩ and |s⟩
    Microsoft.Quantum.Canon.ApplyToEachCA(SWAP, Microsoft.Quantum.Arrays.Zipped(x, s));

    // Reset s
    for idx in 0 .. n - 1 {
      Reset(s[idx]);
    }
  }
}

operation TestQuantumExponent (a : Int, b : Int, n : Int) : Int {
  mutable resultArray = new Result[n];

  use x = Qubit[n] {
    // create qubit's array from integer b (ex: 3 -> |011⟩)
    mutable array2 = new Bool[n];
    mutable tempInt2 = b;
    for idxBit in 0 .. n - 1 {
      set array2 w/= ((n - 1) - idxBit) <- tempInt2 % 2 == 0 ? false | true;
      set tempInt2 = tempInt2 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array2[idx]) {
        X(x[idx]);
      }
    }

    // apply multiplier
    QuantumExponent(a, x);

    // measure and reset
    for idx in 0 .. n - 1 {
      set resultArray w/= idx <- MResetZ(x[idx]);
    }
  }

  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = Microsoft.Quantum.Convert.ResultArrayAsBoolArray(resultArray);
  mutable resultInt = 0;
  for idx in 0 .. n - 1 {
    if(resultBool[idx]) {
      set resultInt = resultInt + (2 ^ ((n - 1) - idx));
    }
  }
  return resultInt;
}

Invoking from Python (Use Python or .NET)

n = 3
a = 3
for b in range(3, 8):
  res = TestQuantumExponent.simulate(a=a, b=b, n=n)
  print("{} ^ {} is {} (mod {})".format(a, b, res, 2**n))

Arithmetic Operations by Modulus N

Finally we consider these operations for arbitrary modulus N (< 2^n ).
For instance, when you specify modulus N = 7 (not 8) for 3 qubit’s integer, 3 (011) + 6 (110) = 2 (010) mod 7.
When you want to use arbitrary modular, it’s a little bit more tricky than above.

Here we assume that N < 2^n (where n = Length(x) = Length(y)) and the integer x and y is less than N.

Note : If x \geq N , set x = x - N (i.e, x mod N ).

The outline of this algorithm is very simple.
Just follow these steps for implementing quantum N-modulus addition. :

  1. First, we add \left| x \right> and \left| y \right> (i.e, \left| x \right> + \left| y \right> ) by above Draper’s adder with no overflow and then we subtract integer N (i.e, \left| x \right> + \left| y \right> - N ).
  2. If x + y < N , the first bit of above result will be 1. Otherwise, it will be 0. (Because N < 2^n and x, y < N .)
    We then turn on ancilla bit with a controlled gate, when this first bit is \left| 1 \right> .
  3. When ancilla bit is \left| 1 \right> , add N back. (You can also implement this transformation with a controlled gate.)
  4. Finally, set ancilla bit to \left| 0 \right> state for clean-up.

But unfortunately, there exists one thing to consider. Same as above QuantumMultiply(), restoring ancilla qubit to \left| 0 \right> state (without using Reset()) is not so easy.
In order to restore ancilla qubit, here we use the following identity in number theory :

(x+y)\:mod\:N \geq x \Leftrightarrow x+y<N

Hence we can check whether x + y - N < 0 or not by subtracting x from the result (x+y)\:mod\:N . Then apply NOT gate to ancilla bit when x + y - N < 0 .

Following is the entire diagram for this circuit.

Note : For details, refer “Circuit for Shor’s algorithm using 2n+3 qubits” (S. Beauregard) for original paper. Or refer “Quantum Algorithms” in Q# document.

Following is Q# code for this N-modulus adder, multiplier, and exponentiation.

Adder, Multiplier, and Exponentiation (Q#)

//
// Implement : |x⟩ |y⟩ -> |x⟩ |x+y mod N⟩ for some integer N
// (where N < 2^n, x < N, y < N)
//
operation QuantumAddByModulus (N : Int, x : Qubit[], y : Qubit[]) : Unit is Adj + Ctl {
  use (ancilla, cx, cy) = (Qubit(), Qubit(), Qubit()) {
    // add bit for preventing overflow
    let x_large = [cx] + x;
    let y_large = [cy] + y;
    // |x⟩ |y⟩ -> |x⟩ |x + y⟩
    QuantumAdd(x_large, y_large);
    // |y⟩ -> |y - N⟩
    Adjoint QuantumAddByNumber(y_large, N);
    // Turn on ancilla when first bit is |1⟩ (i.e, when x + y - N < 0)
    Controlled X([y_large[0]], ancilla);
    // Add N back when ancilla is |1⟩
    Controlled QuantumAddByNumber([ancilla], (y_large, N));
    // set ancilla to |0⟩ (See my above description)
    Adjoint QuantumAdd(x_large, y_large);
    X(ancilla);
    Controlled X([y_large[0]], ancilla);
    QuantumAdd(x_large, y_large);
  }
}

//
// Implement : |y⟩ -> |a y mod N⟩ for some integer a and N
// (where N < 2^n, y < N)
//
// Important Note :
// Integer "a" and "N" must be co-prime number.
// (For making this operator must be controlled. Otherwise InverseModI() raises an error.)
//
operation QuantumMultiplyByModulus (N : Int, a : Int, y : Qubit[]) : Unit is Adj + Ctl {
  let n = Length(y);
  let a_mod = a % N;

  use s = Qubit[n] {
    // start |y⟩ |0⟩

    // apply adder by repeating "a" (integer) times
    for r in 0 .. a_mod - 1 {
      QuantumAddByModulus(N, y, s);
    }
    // now |y⟩ |a y mod N⟩

    // swap first register and second one by tuple
    Microsoft.Quantum.Canon.ApplyToEachCA(SWAP, Microsoft.Quantum.Arrays.Zipped(y, s));
    // now |a y mod N⟩ |y⟩

    // reset all s qubits !
    // but it's tricky because we cannot use "Reset()". (See my above description.)
    let a_inv = InverseModI(a_mod, N);
    for r in 0 .. a_inv - 1 {
      Adjoint QuantumAddByModulus(N, y, s);
    }
  }
}

//
// Implement : |x⟩ -> |a^x mod N⟩ for some integer a and N
// (where N < 2^n)
//
// Important Note :
// Integer "a" and "N" must be co-prime number.
// (Because this invokes QuantumMultiplyByModulus().)
//
operation QuantumExponentByModulus (N : Int, a : Int, x : Qubit[]) : Unit {
  let n = Length(x);
  use s = Qubit[n] {
    // set |s⟩ = |1⟩
    X(s[n - 1]);

    // apply decomposition elements
    for idx in 0 .. n - 1 {
      Controlled QuantumMultiplyByModulus([x[idx]], (N, a^(2^((n-1) - idx)), s));
    }

    // swap |x⟩ and |s⟩
    Microsoft.Quantum.Canon.ApplyToEachCA(SWAP, Microsoft.Quantum.Arrays.Zipped(x, s));

    // Reset s
    for idx in 0 .. n - 1 {
      Reset(s[idx]);
    }
  }
}

Test Start (Q#)

operation TestQuantumAddByModulus (a : Int, b : Int, n : Int, N : Int) : Int {
  mutable resultArray = new Result[n];

  use (x, y) = (Qubit[n], Qubit[n]) {
    // create qubit's array from integer a (ex: 3 -> |011⟩)
    mutable array1 = new Bool[n];
    mutable tempInt1 = a;
    for idxBit in 0 .. n - 1 {
      set array1 w/= ((n - 1) - idxBit) <- tempInt1 % 2 == 0 ? false | true;
      set tempInt1 = tempInt1 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array1[idx]) {
        X(x[idx]);
      }
    }

    // create qubit's array from integer b (ex: 3 -> |011⟩)
    mutable array2 = new Bool[n];
    mutable tempInt2 = b;
    for idxBit in 0 .. n - 1 {
      set array2 w/= ((n - 1) - idxBit) <- tempInt2 % 2 == 0 ? false | true;
      set tempInt2 = tempInt2 / 2;
    }
    for idx in 0 .. n - 1 {
      if(array2[idx]) {
        X(y[idx]);
      }
    }

    // apply Drapper adder
    QuantumAddByModulus(N, x, y);
    //QuantumMultiplyByModulus(N, a, y);
    //Adjoint QuantumAddByModulus(N, x, y);
    //QuantumExponentByModulus(N, a, y);

    // measure and reset
    for idx in 0 .. n - 1 {
      set resultArray w/= idx <- MResetZ(y[idx]);
    }
    for idx in 0 .. n - 1 {
      Reset(x[idx]);
    }
  }

  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = Microsoft.Quantum.Convert.ResultArrayAsBoolArray(resultArray);
  mutable resultInt = 0;
  for idx in 0 .. n - 1 {
    if(resultBool[idx]) {
      set resultInt = resultInt + (2 ^ ((n - 1) - idx));
    }
  }
  return resultInt;
}

Call Test from Python (Use Python or .NET)

n = 3
N = 7
a = 3
for b in range(3, 7):
  res = TestQuantumAddByModulus.simulate(a=a, b=b, n=n, N=N)
  print("{} + {} is {} (mod {})".format(a, b, res, N))

You can also use built-in IncrementByModularInteger() and MultiplyByModularInteger() in Q# library. (Note that these built-in operations use little endian order unlike my source code.)

 

In order to help you understand, here I’m calling QFT and QFT^{-1} every time in each operations, but you can speed up by invoking QFT / QFT^{-1} once and merging phase operations. (My source code is calling so many QFTs, but you can reduce these duplicate transformation.)
See “Circuit for Shor’s algorithm using 2n + 3 qubits” or “Fast Quantum Modular Exponentiation Architecture” for more optimized algorithms.

Now we’re ready to implement Shor’s algorithm.
In the next post, we’ll see the most famous quantum algorithm, Shor’s algorithm, for numerical integer factorization.

 

Source Code :
https://github.com/tsmatz/quantum-algorithms-qsharp/blob/master/04-arithmetic-operations.ipynb

Reference :
Quantum arithmetic with the Quantum Fourier Transform

 

Update History :

May 2019 Added description for arbitrary modulus operation (for Shor’s algorithm)

 

Categories: Uncategorized

Tagged as:

5 replies »

Leave a Reply