Uncategorized

Shor’s Algorithm – Quantum Period Finding (Q#)

(Download source code from here.)

For the last post of this series, I’ll show you the most famous quantum algorithm, Shor’s algorithm, with Q# implementation.

Series : Quantum algorithm’s implementation (Q#)

In classical method, you needs exponential time computation (of the input’s bit size) for solving integer factorization, but you can solve with polynomial time computation by Shor’s algorithm.
This algorithm would have significant impacts for today’s computing, because current cryptographic technology depends on the difficulty of large composite integer’s factorization in classical approaches, and a lot of computing technologies (such as, security, identity, blockchain, etc) then depend on this current cryptographic technology.

Note : For this reason, quantum cryptography and key distribution (see here) is one of big concerns in today’s quantum studies and researches.

As well as other posts in this series, first I’ll describe the outline (idea) of this algorithm, and later I’ll show you Q# code along with this idea.

Before starting this post, I recommend you to read my early posts in this series (especially, “Quantum Fourier Transform (QFT) / Phase Estimation” and “Quantum Arithmetic“), if you’re not familiar with quantum methods.

Quantum Period-Finding (Shor’s Algorithm)

If positive integer N and a is coprime, number theory tells us there exists r > 1 such as a^r = 1\:mod\:N . We now assume that r is positive least number such that a^r = 1\:mod\:N .
As you can see :

a^{r+b}\:mod\:N = (a^r \cdot a^b)\:mod\:N = a^b\:mod\:N

Therefore a^x\:mod\:N is periodic (cyclic) for input x by r -cycle. (If there exists more least integer r_0 < r such that a^{x_0} = a^{x_0+r_0}\:mod\:N , this implies a^{r_0} = 1\:mod\:N and r is not then the least integer.)
For instance, if N = 11 and a = 5 , then a^x\:mod\:N\;(x=1, 2, \ldots) equals to 5, 3, 4, 9, 1, 5, 3, 4, 9, 1, 5, 3, … In this case, the period length r is 5.

5^1\:mod\:11 = 5
5^2\:mod\:11 = 3
5^3\:mod\:11 = 4
5^4\:mod\:11 = 9
5^5\:mod\:11 = 1
5^6\:mod\:11 = 5
5^7\:mod\:11 = 3
5^8\:mod\:11 = 4

This period r is called “order” of a modulus N .

Our concern is how to find the period (order) for given coprime integers N > 1 and a > 1 .
Of course, you can iteratively search by calculating with 1 to N - 1 , but it needs O(2^n) computation time (i.e, exponential time), where the integer N has n bit’s size.
Here we want more efficient algorithms, and Peter Shor has showed us polynomial-time algorithm with quantum computer as follows.

The outline of Shor’s algorithm is : transform to all possible values using quantum superpositions, apply some N modulus operations for these qubits, and then finally estimate the period r using measured values.
Now let’s see the details of this algorithm and see why this makes things.

In this algorithm, we need the following 2 register :

  • The first register \left| x \right> has n qubits, in which n satisfies N^2 \leq 2^n < 2 N^2 .
  • The second register \left| y \right> has at least \log_2 N qubits for storing \left|a^x\:mod\:N\right> .

To simplify explanation, here we assume that 2^n/r is some integer m , i.e, 2^n/r=m . (Later we will discuss about the case in which 2^n/r is not an integer…)
Note that r is unknown for now.

First we generate the superposition for x . (i.e, \left| x \right> \to H^{\otimes n} \left| x \right> )
Then the state of \left| x \right> is now :

\displaystyle \frac{1}{\sqrt{2^n}} \sum_{i=0}^{2^n - 1} \left| i \right>

Next we transform \left| y \right> (which is initialized by \left| 0 \right> ) as follows.
For this implementation, please see my previous post “Quantum Arithmetic (Q#)“.

\left| x \right> \left| y \right> = \left| x \right> \left| 0 \right> \to \left| x \right>\left|a^x\:mod\:N\right>

Now we measure the register \left| y \right> . Once we’ve measured \left| y \right> , the register \left| y \right> is garbage and we don’t need \left| y \right> any more.

After you’ve measured the register \left| y \right> , \left| y \right> results into \left|a^{x_0}\:mod\:N\right> for some x_0 .
Hence \left| x \right> results into the following state, because 2^n/r=m .

\displaystyle \frac{1}{\sqrt{m}} \sum_{j=0}^{m-1} \left| x_0 + jr \right>

Note : See here for quantum measurement.

Now we apply Quantum Fourier Transform (QFT) for register \left| x \right> , and \left| x \right> then results into the following state. (See this post for QFT.) :

\displaystyle \frac{1}{\sqrt{2^n}} \frac{1}{\sqrt{m}} \sum_{k=0}^{2^n-1} \sum_{j=0}^{m-1} \exp(\frac{2\pi i (x_0+jr)k}{2^n}) \left| k \right>

\displaystyle = \frac{1}{\sqrt{2^n}} \frac{1}{\sqrt{m}} \sum_{k=0}^{2^n-1} \Biggl[ \sum_{j=0}^{m-1} \exp(\frac{2\pi ijrk}{2^n}) \Biggr] \exp(\frac{2\pi i x_0 k}{2^n}) \left| k \right>

\displaystyle = \frac{1}{\sqrt{2^n}} \frac{1}{\sqrt{m}} \sum_{k=0}^{2^n-1} \Biggl[ \sum_{j=0}^{m-1} \exp(\frac{2\pi ijk}{m}) \Biggr] \exp(\frac{2\pi i x_0 k}{2^n}) \left| k \right>

Now let me fix some k ( k \in \{0, \ldots ,2^n-1\} ) such that k/m is not integer. In this case, the terms in the square brackets in the above equation cancels each others and then results into 0, because m = 2^p for some integer p .
As a result, only terms with k , in which k/m is integer, resides in the above equation.

Next let me fix some k ( k \in \{0, \ldots ,2^n-1\} ) such that k/m is integer. In this case, \exp(\frac{2\pi ijk}{m}) equals to 1. And the number of such k will then be r , such as 0,\:\frac{2^n}{r},\:\frac{2^n}{r} \times 2,\:\ldots\:,\:\:\frac{2^n}{r} \times (r - 1) .
Therefore we can replace above equation with k = \frac{2^n}{r} \times l \;\; (l = 0,\:\ldots\:, r-1) , and we then get :

\displaystyle = \frac{1}{\sqrt{2^n}} \frac{1}{\sqrt{m}} \sum_{l=0}^{r-1} m \exp(\frac{2\pi i x_0 \times 2^n l / r}{2^n}) \left| \frac{2^n l}{r} \right>

\displaystyle = \frac{1}{\sqrt{r}} \sum_{l=0}^{r-1} \exp(\frac{2\pi i x_0 l}{r}) \left| \frac{2^n l}{r} \right>

Now we measure \left| x \right> and assume that the results is some integer C .

As you can easily see, we get C = \frac{2^n l_0}{r} for some unknown integer l_0 .
If l_0 and r is coprime, you can get r using greatest common divisor (shortly, GCD) by Euclidean algorithm with known C and 2^n .

But it’s not so simple. Please remind our assumption : 2^n = mr . For instance, when r is odd, you cannot simply get r by GCD.
When 2^n \neq mr , it’s known that r is approximated by the following r^{\prime} using continued fraction expansion. If the following r^{\prime} and d is coprime, then you can get r=r^{\prime} . If it’s not coprime (check if the result r^{\prime} is an order or not), you repeat this algorithm until you find an order r . (Here I don’t describe about this approximation, but please refer p.18 in the original paper “Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer” by Peter Shor.)

\displaystyle \left| \frac{C}{2^n} - \frac{d}{r^{\prime}} \right| < \frac{1}{2 \cdot 2^n}
where r^{\prime} < N and d \in \mathbb{N}

Note : For approximation, there exists another option, which uses phase estimation.
See “Applications – Shor’s Algorithm” in Q# document for details.

Number theory tells us that this takes place with probability at least 1 / \log \log r (see Hardy-Ramanujan theorem), and the entire period-fining (order-finding) has O(n^2 \log n \log \log n) computation complexity. That is, this algorithm has polynomial computation complexity of the input size n .

Programming Quantum Period-Finding

Now let’s start Q# programming along with above algorithm.

Note : As I have mentioned in previous post, my code is written straightforward without any optimization, in order to help you understand the algorithm’s outline. (e.g, So many QFTs are called inside my function.)
Once you have learned core concept in this post, please proceed to optimize your code.

First we prepare the register \left| x \right> and \left| y \right> , which states are all initialized as \left| 0 \right> .

// Get least integer n1 such as : num^2 <= 2^n1
let n1 = BitSizeI(num) * 2;
let n2 = BitSizeI(num);
...

use (x, y) = (Qubit[n1], Qubit[n2]) {
  ...
}

We apply \left| x \right> \to H^{\otimes n} \left| x \right> and generate superposition as follows.

...
use (x, y) = (Qubit[n1], Qubit[n2]) {
  Microsoft.Quantum.Canon.ApplyToEachCA(H, x);
  ...
}

Next we apply \left| x \right> \left| y \right> = \left| x \right> \left| 0 \right> \to \left| x \right>\left|a^x\:mod\:N\right> in Q#.

In previous post, we’ve already learned and implemented this algorithms.
The following QuantumMultiplyByModulus() is the operation which I have already built in previous post.
The following QuantumExponentForPeriodFinding() is a modified version of QuantumExponentByModulus() in previous post.

...
use (x, y) = (Qubit[n1], Qubit[n2]) {
  Microsoft.Quantum.Canon.ApplyToEachCA(H, x);

  // |x⟩ |0 (=y)⟩ -> |x⟩ |a^x mod N⟩
  QuantumExponentForPeriodFinding(a, num, x, y);
  ...
}
...
// Implement : |x⟩ |0 (=y)⟩ -> |x⟩ |a^x mod N⟩ for some integer a
// (where y should be |0⟩)
// This is modified version of QuantumExponentByModulus() in my post.
// See https://tsmatz.wordpress.com/2019/05/22/quantum-computing-modulus-add-subtract-multiply-exponent/
operation QuantumExponentForPeriodFinding (a : Int, N : Int, x : Qubit[], y : Qubit[]) : Unit {
  let n1 = Length(x);
  let n2 = Length(y);

  // set |y⟩ = |0...01⟩
  X(y[n2 - 1]);

  for idx in 0 .. n1 - 1 {
    // a^(2^((n1-1) - idx)) is too big, then we reduce beforehand
    mutable a_mod = 1;
    for power in 1 .. 2^((n1-1) - idx) {
      set a_mod = (a_mod * a) % N;
    }
    // apply decomposition elements
    Controlled QuantumMultiplyByModulus([x[idx]], (N, a_mod, y));
  }
}

Next we measure the register \left| y \right> and apply Quantum Fourier Transform (QFT) to the register \left| x \right> .
I note that the following QFTImpl() operation is the one which I have already built in this post. (You can also use Q# built-in operation for QFT.)

  ...
  
  // measure y and reset
  mutable tmpResult = new Result[n2];
  for idx in 0 .. n2 - 1 {
    set tmpResult w/= idx <-MResetZ(y[idx]);
  }

  // QFT for x
  QFTImpl(x);
  ...

Now we measure the register \left| x \right> , and set results into variable “realResult“.

  // Measure x and reset
  mutable realResult = new Result[n1];
  for idx in 0 .. n1 - 1 {
    set realResult w/= idx <-MResetZ(x[idx]);
  }

As I have mentioned above, we should get approximated fraction d / r^{\prime} (where r^{\prime} \leq N ) from the fraction C / 2^n (where C is measured value, i.e, above “realResult“) by performing continued fraction expansion.
Here we use Microsoft.Quantum.Math.ContinuedFractionConvergentL() in Q# library for continued fraction, but you can also implement Euclidean by yourself. (We have also used Microsoft.Quantum.Math.GreatestCommonDivisorL() in Q# for GCD.)

  ...
  
  // get integer's result from measured array (ex : |011⟩ -> 3)
  let resultBool = [false] + Microsoft.Quantum.Convert.ResultArrayAsBoolArray(realResult); // for making unsigned positive integer, add first bit
  let resultBool_R = Microsoft.Quantum.Arrays.Reversed(resultBool); // because BoolArrayAsBigInt() is Little Endian order
  let resultIntL = Microsoft.Quantum.Convert.BoolArrayAsBigInt(resultBool_R);

  // get period candidate by continued fraction expansion (thanks to Euclid !)
  let gcdL = GreatestCommonDivisorL(resultIntL, 2L^n1);
  let calculatedNumerator = resultIntL / gcdL;
  let calculatedDenominator = 2L^n1 / gcdL;
  let numL = Microsoft.Quantum.Convert.IntAsBigInt(num);
  let approximatedFraction =
    ContinuedFractionConvergentL(BigFraction(calculatedNumerator, calculatedDenominator), numL);
  let (approximatedNumerator, approximatedDenominator) = approximatedFraction!;
  mutable periodCandidateL = 0L;
  if(approximatedDenominator < 0L) {
    set periodCandidateL = approximatedDenominator * -1L;
  }
  else {
    set periodCandidateL = approximatedDenominator;       
  }
  set periodCandidate = ReduceBigIntToInt(periodCandidateL);

  // output for debugging
  Message($"Measured Fraction : {resultIntL} / {2L^n1}");
  Message($"Approximated Fraction : {approximatedNumerator} / {approximatedDenominator}");
  Message($"Period Candidate : {periodCandidate}");
  ...
// This is helper function to convert BigInt to Int ...
operation ReduceBigIntToInt(numL : BigInt) : Int {
  // Check if numL is not large
  Microsoft.Quantum.Diagnostics.Fact(BitSizeL(numL) <= 32, $"Cannot convert to Int. Input is too large");

  mutable resultInt = 0;
  let numArray = Microsoft.Quantum.Convert.BigIntAsBoolArray(numL);
  let numArray_R = Microsoft.Quantum.Arrays.Reversed(numArray); // because BigIntAsBoolArray() is Little Endian order
  let nSize = Length(numArray_R);
  for idx in 0 .. nSize - 1 {
    if(numArray_R[idx] and ((nSize - 1) - idx <= 31)) {
      set resultInt = resultInt + (2 ^ ((nSize - 1) - idx));
    }
  }
  return resultInt;
}

Finally you check if r^{\prime} (above “periodCandidate”) is the period or not. If not, please repeat once again.

 

The completed Q# code is as follows.

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

operation QuantumPeriodFinding (num : Int, a : Int) : Unit {
  // Get least integer n1 such as : num^2 <= 2^n1
  let n1 = BitSizeI(num) * 2;
  let n2 = BitSizeI(num);
  mutable periodCandidate = 1;
  repeat {
    use (x, y) = (Qubit[n1], Qubit[n2]) {
      Microsoft.Quantum.Canon.ApplyToEachCA(H, x);

      // |x⟩ |0 (=y)⟩ -> |x⟩ |a^x mod N⟩
      QuantumExponentForPeriodFinding(a, num, x, y);

      // measure y and reset
      mutable tmpResult = new Result[n2];
      for idx in 0 .. n2 - 1 {
        set tmpResult w/= idx <-MResetZ(y[idx]);
      }

      // QFT for x
      QFTImpl(x);

      // Measure x and reset
      mutable realResult = new Result[n1];
      for idx in 0 .. n1 - 1 {
        set realResult w/= idx <-MResetZ(x[idx]);
      }
      
      // get integer's result from measured array (ex : |011⟩ -> 3)
      let resultBool = [false] + Microsoft.Quantum.Convert.ResultArrayAsBoolArray(realResult); // for making unsigned positive integer, add first bit
      let resultBool_R = Microsoft.Quantum.Arrays.Reversed(resultBool); // because BoolArrayAsBigInt() is Little Endian order
      let resultIntL = Microsoft.Quantum.Convert.BoolArrayAsBigInt(resultBool_R);

      // get period candidate by continued fraction expansion (thanks to Euclid !)
      let gcdL = GreatestCommonDivisorL(resultIntL, 2L^n1);
      let calculatedNumerator = resultIntL / gcdL;
      let calculatedDenominator = 2L^n1 / gcdL;
      let numL = Microsoft.Quantum.Convert.IntAsBigInt(num);
      let approximatedFraction =
        ContinuedFractionConvergentL(BigFraction(calculatedNumerator, calculatedDenominator), numL);
      let (approximatedNumerator, approximatedDenominator) = approximatedFraction!;
      mutable periodCandidateL = 0L;
      if(approximatedDenominator < 0L) {
        set periodCandidateL = approximatedDenominator * -1L;
      }
      else {
        set periodCandidateL = approximatedDenominator;       
      }
      set periodCandidate = ReduceBigIntToInt(periodCandidateL);

      // output for debugging
      Message($"Measured Fraction : {resultIntL} / {2L^n1}");
      Message($"Approximated Fraction : {approximatedNumerator} / {approximatedDenominator}");
      Message($"Period Candidate : {periodCandidate}");
    }
  }
  until ((periodCandidate != 0) and (ExpModI(a, periodCandidate, num) == 1))
  fixup {
  }

  // output for debugging
  Message("Found period " + Microsoft.Quantum.Convert.IntAsString(periodCandidate));
  Message("");
}

// Implement : |x⟩ |0 (=y)⟩ -> |x⟩ |a^x mod N⟩ for some integer a
// (where y should be |0⟩)
// This is modified version of QuantumExponentByModulus() in my post.
// See https://tsmatz.wordpress.com/2019/05/22/quantum-computing-modulus-add-subtract-multiply-exponent/
operation QuantumExponentForPeriodFinding (a : Int, N : Int, x : Qubit[], y : Qubit[]) : Unit {
  let n1 = Length(x);
  let n2 = Length(y);

  // set |y⟩ = |0...01⟩
  X(y[n2 - 1]);

  for idx in 0 .. n1 - 1 {
    // a^(2^((n1-1) - idx)) is too big, then we reduce beforehand
    mutable a_mod = 1;
    for power in 1 .. 2^((n1-1) - idx) {
      set a_mod = (a_mod * a) % N;
    }
    // apply decomposition elements
    Controlled QuantumMultiplyByModulus([x[idx]], (N, a_mod, y));
  }
}

// This is helper function to convert BigInt to Int ...
operation ReduceBigIntToInt(numL : BigInt) : Int {
  // Check if numL is not large
  Microsoft.Quantum.Diagnostics.Fact(BitSizeL(numL) <= 32, $"Cannot convert to Int. Input is too large");

  mutable resultInt = 0;
  let numArray = Microsoft.Quantum.Convert.BigIntAsBoolArray(numL);
  let numArray_R = Microsoft.Quantum.Arrays.Reversed(numArray); // because BigIntAsBoolArray() is Little Endian order
  let nSize = Length(numArray_R);
  for idx in 0 .. nSize - 1 {
    if(numArray_R[idx] and ((nSize - 1) - idx <= 31)) {
      set resultInt = resultInt + (2 ^ ((nSize - 1) - idx));
    }
  }
  return resultInt;
}

You can invoke this code (Q#) from your Python code as follows. (Use Python or .NET .)

N = 11
a = 5
res = QuantumPeriodFinding.simulate(num=N, a=a)

N = 15
a = 7
res = QuantumPeriodFinding.simulate(num=N, a=a)

You can download and run source code from here.

Integer Factorization

Using above period-finding algorithm, we can now extend to integer factorization, which is also difficult to be solved by classical efficient algorithms.

Note : As you can easily see, it needs O(2^n) when you apply the trivial iteration search for integer factorization, where the integer has n bit’s size. There exist several challenges (studies) for reducing computation complexity with classical algorithms, but it is known that there’s no existence with n -polynomial complexity for a n -bits number in classical algorithms. (See “Integer factorization” in Wikipedia.)

When r is even and a^{r/2}\:mod\:N \neq N - 1 , it’s known that at least one of \verb|gcd|(N, a^{r/2} \pm 1) is a non-trivial factor of N . Then overall algorithm of integer factorization for integer N is as follows. :
(The following step 5 is quantum step, and others are classical.)

  1. If N is even, return 2 as a factor.
    If not, proceed to the next step.
  2. Check if N = j^k for some integer j > 1 and k > 1 . (Check \sqrt[k]{N} for all k \leq \log_2 N . This will be n -polynomial computation for n -bit’s integer N .)
    If so, return j = \sqrt[k]{N} as a factor. Otherwise, proceed to the next step.
  3. Pick a random integer 1 < a < N and get the great common divisor \verb|gcd|(N,a) with Euclidean algorithm.
  4. If \verb|gcd|(N,a) > 1 , return \verb|gcd|(N,a) as a factor.
    If not, proceed to the next step.
  5. If \verb|gcd|(N,a) = 1 , N and a is coprime and then run above quantum period-finding algorithm for N and a . (i.e, find the least integer r such as a^r\:mod\:N = 1 .)
  6. If the period r is odd, go back to step 3.
  7. If the period r is even and a^{r/2}\:mod\:N = N - 1 , go back to step 3.
  8. Otherwise, check if \verb|gcd|(a^{r/2} \pm 1,N) is a nontrivial factor of N . If not, go back to step 3.

Please try to implement quantum-inspired integer factorization with the combination of quantum part (above Q# code) and classical part (Python code).
(Here I skip this implementation example.)

 

In today’s gate-model quantum computers, there exist difficulties for error correction (correction of quantum noise) and fault tolerance, and the scaling up qubits (i.e, adding more qubits) will then increase the errors more.
Considering these facts, Shor’s algorithm for large number is hard to be implemented in today’s devices, and unfortunately example in this post will then be only for experimental purpose. (It won’t be applicable to the real business.)

We hope that we can make use of general-purpose (gate-model) quantum computers in the near future.

 

Reference :

Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer

Principles of Quantum Computation and Information

 

Categories: Uncategorized

Tagged as:

6 replies »

  1. Very nice example! Do you have your posts as pdf files – for me it is more convenient to read some longer publications on paper than on a computer. Thank you!

    Like

Leave a Reply