Simon's Algorithm with Amazon Braket
Overview
# --- Setup cell added by QCR (not part of the original tutorial) ---
# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.
# Installs the example's dependencies. If a later cell still reports a missing
# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 matplotlib
Simon's Algorithm
Abstract: We study a quantum algorithm known as Simon's algorithm, which provided the first example of an exponential speedup over the best known classical algorithm by using a quantum computer to solve a particular problem. Originally published in 1994, Simon's algorithm was a precursor to Shor's well-known factoring algorithm, and it served as inspiration for many of the seminal works in quantum computation that followed.
This notebook is aimed at users with some basic knowledge of quantum computing and quantum circuits.
Table of Contents
- Simon's Problem Statement
- Example for n=3
- Classical Complexity
- Quantum Algorithm for Simon's Problem
- Quantum Circuit
- Running Simon's Algorithm
- Classical Post-Processing
- Quantum Complexity
- Implementing Simon's Algorithm in Amazon Braket
- References
- Appendix
- Implementing an Oracle Function
- Implementing the Classical Post-Processing
Simon's Problem Statement:
Suppose we’re given a function
Said another way, there exists an unknown string
The goal of Simon's problem is to determine if
Since we're given the promise that
Example for n=3:
Consider the function
By inspection, we can see that
For concreteness, notice that the input strings
Therefore, in this example, the secret string is
In this specific example, we also see that the string
Classical Complexity
To solve Simon's problem classically, one needs to find two different inputs
It turns out that a lower bound on the classical query complexity of Simon's algorithm can also be found:
As mentioned above, the goal is to find a pair of input strings
Quantum Algorithm for Simon's Problem
Simon's algorithm is a scheme for solving the problem above using exponentially fewer queries to the function
Quantum Circuit
Simon's algorithm involves both quantum and classical components. The quantum part of Simon's algorithm is used to query the oracle efficiently, while the classical component is used to process measurement results and determine the hidden string
For a function
Running Simon's Algorithm
To solve Simon's problem, one needs to run the quantum circuit above several times. After each run of the circuit, the measurements of the first
An analysis of the circuit above shows that each output bit string
Let us now analyze the above circuit step-by-step:
-
Initialize all qubits in the
state. That is, we start in the state . We will use the shorthand -
Apply Hadamard gates to each of the first
qubits, placing them in the equal superposition state: . -
Apply the oracle
, which computes the function into the last qubits, giving the state -
Measure the last
A.qubits, giving a random result . If is one-to-one, this output of corresponds to an input of . If is two-to-one, the output of corresponds to an input of either either or , where and are the two different inputs to that gave the same output . Hence we are left with the first qubits in the state; , if is one-to-one; B. , where , if is two-to-one. Note that this step is not strictly necessary, since we do not need the measurement result, but we include it as it makes the analysis easier. -
Apply Hadamard gates to each of the first
qubits. If is one-to-one, the state is mapped to where is the dot product between the two strings represented as vectors (modulo 2). Similarly, if
is two-to-one, the state is mapped to . -
Measure the first
qubits. - If
is one-to-one, measurements return a random bit string uniformly chosen from . - If
is two-to-one, measurements return a random bit string such that , since otherwise the amplitude cancels out. Using the criterion for Simons problem (i.e,. ), we find that
Thus, in both cases we obtain a random bit string
such that . - If
Therefore, each time we run the quantum circuit above, we find a bit string
Classical Post-Processing
From the measurement results
There are
Quantum Complexity
How many queries do we need to make to
Comparing the quantum and classical algorithms, we saw that the classical algorithm requires at least
# Imports and Setup
import matplotlib.pyplot as plt
import numpy as np
from braket.circuits import Circuit
from braket.devices import LocalSimulator
%matplotlib inline
# Sets the device to run the circuit on
device = LocalSimulator()We also import a method called simons_oracle, which generates a circuit implementing an example oracle function. The code for simons_oracle is defined in the simons_utils.py module, and it is shown in the Appendix for completeness.
# Import local utils
from simons_utils import simons_oracle # noqa: F401We now define the secret string,
s = "101011"
# Other examples to try:
# s = '011'
# s = '00000'
# s = '1'
# Generate a random string of random length from 1 to 10:
# s="".join(str(np.random.randint(2)) for _ in range(np.random.randint(1,10)))
print("The secret string is: " + s)The secret string is: 101011
Circuit Definition
We now define the quantum circuit for Simon's algorithm:
-
Apply Hadamard gates to the first
-qubits. -
Query the oracle (i.e., the
gate). In this example, the oracle is defined dynamically, based on our chosen value of . You can try experimenting with different values of (with differing lengths). -
Apply Hadamard gates to the first
-qubits.
n = len(s)
circ = Circuit()
# Apply Hadamard gates to first n qubits
circ.h(range(n))
# Now apply the Oracle for f
circ.simons_oracle(s)
# Apply Hadamard gates to the first n qubits
circ.h(range(n))
print(circ)T : |0| 1 | 2 |3|4|5|6|
q0 : -H-C-----------C---C-C-C-H-
| | | | |
q1 : -H-|-C---------|-H-|-|-|---
| | | | | |
q2 : -H-|-|-C-------|-H-|-|-|---
| | | | | | |
q3 : -H-|-|-|-C-----|-H-|-|-|---
| | | | | | | |
q4 : -H-|-|-|-|-C---|-H-|-|-|---
| | | | | | | | |
q5 : -H-|-|-|-|-|-C-|-H-|-|-|---
| | | | | | | | | |
q6 : ---X-|-|-|-|-|-X---|-|-|---
| | | | | | | |
q7 : -----X-|-|-|-|-----|-|-|---
| | | | | | |
q8 : -------X-|-|-|-----X-|-|---
| | | | |
q9 : ---------X-|-|-------|-|---
| | | |
q10 : -----------X-|-------X-|---
| |
q11 : -------------X---------X---
T : |0| 1 | 2 |3|4|5|6|
Now run the circuit
We need enough shots to obtain 4n shots in the example below, just to be on the safe side.
task = device.run(circ, shots=4 * n)Analyze the results
We can retrieve the measurement results on all
result = task.result()
counts = result.measurement_counts
plt.bar(counts.keys(), counts.values())
plt.xlabel("bit strings")
plt.ylabel("counts")
plt.xticks(rotation=90)
plt.show()Aggregate the results
The measurements are performed on all
new_results = {}
for bitstring, count in result.measurement_counts.items():
# Only keep the outcomes on first n qubits
trunc_bitstring = bitstring[:n]
# Add the count to that of the of truncated bit string
new_results[trunc_bitstring] = new_results.get(trunc_bitstring, 0) + count
plt.bar(new_results.keys(), new_results.values())
plt.xlabel("bit strings")
plt.ylabel("counts")
plt.xticks(rotation=70)
plt.show()In practice, we only need the measurement results (i.e., the bit strings, not the counts) from the first
With these
Note that we may have too many bit strings in the above, since we ran the quantum task with
At this stage, the quantum portion of Simon's algorithm is complete. Any remaining steps are just classical postprocessing, which we cover in the Appendix.
References
[1] Wikipedia: Simon's Problem
[2] Wikipedia: Birthday Problem
[3] Wikipedia: Computing a kernel by Gaussian elimination
[4] StackExchange: Sympy: Solving Matrices in a finite field
In order to run the algorithm, we will need a unitary function that we can use as an oracle to query the function
There are many possible ways of implementing a function with the desired property that
Classical Intuition Behind the Function
Generating a function that is one-to-one is conceptually straightforward, as any such function of the bit strings
We will implement one simple choice for
Our approach will be to choose a flag bit in the input bit strings that we will use to split the inputs. We then
Example for n=3:
We now revisit the example in the introduction. Suppose the secret string
We leave it as an exercise to the reader to verify that this definition works for any input string size (i.e.,
Quantum Implementation of
We now define the unitary using the @circuit.subroutine functionality of the Amazon Braket SDK. The following code was imported from the simons_utils.py module, and is shown below for reference.
In the quantum setting, we first copy the input register into some ancillary qubits:
from braket.circuits import Circuit, circuit
@circuit.subroutine(register=True)
def simons_oracle(secret_s: str):
"""
Quantum circuit implementing a particular oracle for Simon's problem. Details of this implementation are
explained in the Simons Algorithm demo notebook.
Args:
secret_s (str): secret string we wish to find
"""
# Find the index of the first 1 in s, to be used as the flag bit
flag_bit=secret_s.find('1')
n=len(secret_s)
circ = Circuit()
# First copy the first n qubits, so that |x>|0> -> |x>|x>
for i in range(n):
circ.cnot(i, i+n)
# If flag_bit=-1, s is the all-zeros string, and we do nothing else.
if flag_bit != -1:
# Now apply the XOR with s whenever the flag bit is 1.
for index,bit_value in enumerate(secret_s):
if bit_value not in ['0','1']:
raise Exception ('Incorrect char \'' + bit_value + '\' in secret string s:' + secret_s)
# XOR with s whenever the flag bit is 1.
# In terms of gates, XOR means we apply an X gate only whenever the corresponding bit in s is 1.
# Applying this X only when the flag qubit is 1 means this is a CNOT gate.
if(bit_value == '1'):
circ.cnot(flag_bit,index+n)
return circ
from sympy import Matrix[notice] A new release of pip available: 22.3.1 -> 23.2.1 [notice] To update, run: pip install --upgrade pip
Generate a matrix from the bit string outputs. We first check that we have sufficiently many output strings to be able to solve the system of equations. If not: output and error and re-run the algorithm.
if len(new_results.keys()) < len(s):
raise Exception(
"System will be underdetermined. Minimum "
+ str(n)
+ " bistrings needed, but only "
+ str(len(new_results.keys()))
+ " returned. Please rerun Simon's algorithm.",
)
string_list = [[int(c) for c in key] for key in new_results]
print("The result in matrix form is :")
for a in string_list:
print(a)The result in matrix form is : [1, 0, 0, 0, 1, 0] [1, 1, 0, 1, 1, 0] [1, 1, 1, 1, 0, 0] [1, 1, 1, 0, 0, 0] [0, 0, 1, 1, 1, 0] [1, 0, 1, 0, 1, 1] [1, 0, 1, 1, 0, 0] [1, 0, 0, 1, 1, 0] [0, 1, 1, 1, 0, 1] [0, 1, 1, 0, 0, 1] [0, 0, 0, 1, 0, 0] [1, 0, 1, 0, 0, 0] [1, 1, 0, 0, 0, 1] [1, 0, 0, 0, 0, 1] [1, 1, 1, 1, 1, 1]
M = Matrix(string_list).T
# Construct the agumented matrix
M_I = Matrix(np.hstack([M, np.eye(M.shape[0], dtype=int)]))
# Perform row reduction, working modulo 2. We use the iszerofunc property of rref
# to perform the Gaussian elimination over the finite field.
M_I_rref = M_I.rref(iszerofunc=lambda x: x % 2 == 0)
# In row reduced echelon form, we can end up with a solution outside of the finite field {0,1}.
# Thus, we need to revert the matrix back to this field by treating fractions as a modular inverse.
# Since the denominator will always be odd (i.e. 1 mod 2), it can be ignored.
# Helper function to treat fractions as modular inverse:
def mod2(x):
return x.as_numer_denom()[0] % 2
# Apply our helper function to the matrix
M_I_final = M_I_rref[0].applyfunc(mod2)
# Extract the kernel of M from the remaining columns of the last row, when s is nonzero.
if all(value == 0 for value in M_I_final[-1, : M.shape[1]]):
result_s = "".join(str(c) for c in M_I_final[-1, M.shape[1] :])
# Otherwise, the sub-matrix will be full rank, so just set s=0...0
else:
result_s = "0" * M.shape[0]
# Check whether result_s is equal to initial s:
print("Secret string: " + s)
print("Result string: " + result_s)
if result_s == s:
print("We found the correct answer.")
else:
print("Error. The answer is wrong!")Secret string: 101011 Result string: 101011 We found the correct answer.
This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.
Versions
Cite all versions? Use the base QCR ID to always reference the latest version of this entry.
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!