From 64bcc590af3d182ec334717274a1193ca25de3ab Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Wed, 6 Jul 2022 13:57:35 -0700 Subject: [PATCH 1/3] Minor updates to development page - Change Foxtail to Sycamore since Foxtail is deprecated. - Add some clarification that instructions are for linux. --- docs/dev/development.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/dev/development.md b/docs/dev/development.md index 3c8695be013..78e67e5da7c 100644 --- a/docs/dev/development.md +++ b/docs/dev/development.md @@ -28,12 +28,12 @@ Note that if you are using PyCharm, you might have to Restart & Invalidate Cache ```bash docker build -t cirq --target cirq_stable . - docker run -it cirq python -c "import cirq_google; print(cirq_google.Foxtail)" + docker run -it cirq python -c "import cirq_google; print(cirq_google.Sycamore23)" ``` ```bash docker build -t cirq_pre --target cirq_pre_release . - docker run -it cirq_pre python -c "import cirq_google; print(cirq_google.Foxtail)" + docker run -it cirq_pre python -c "import cirq_google; print(cirq_google.Sycamore23)" ``` If you want to contribute changes to Cirq, you will instead want to fork the repository and submit pull requests from your fork. @@ -85,6 +85,9 @@ At this point your local git master should be synced with the master from the ma ## Setting up an environment +These instructions are primarily for linux-based environments that use the apt +package manager. + 0. First clone the repository, if you have not already done so. See the previous section for instructions. From d936b8e1756ccaedd4c5865c4d5ede3147766f18 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Sun, 10 Jul 2022 16:04:06 -0700 Subject: [PATCH 2/3] Improve Fourier Checking tutorial - Miscellaenous fixes for Fourier Checking - Fix top block was code not text - Fix latex blocks in various places - Improve docstrings and typing for functions - A few miscellaenous wording improvements. --- docs/experiments/fourier_checking.ipynb | 239 ++++++++++++++---------- 1 file changed, 140 insertions(+), 99 deletions(-) diff --git a/docs/experiments/fourier_checking.ipynb b/docs/experiments/fourier_checking.ipynb index aab91a5adca..d31a65c8016 100644 --- a/docs/experiments/fourier_checking.ipynb +++ b/docs/experiments/fourier_checking.ipynb @@ -32,12 +32,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": { - "id": "6acab4c131af" + "id": "ytiXnAqTUBrB" }, - "outputs": [], "source": [ "# Fourier Checking Problem" ] @@ -72,6 +70,7 @@ }, "outputs": [], "source": [ + "# Initial setup to install cirq and set up dependencies for the tutorial.\n", "try:\n", " import cirq\n", "except:\n", @@ -80,10 +79,12 @@ " print(\"installed cirq.\")\n", " import cirq\n", "\n", + "from typing import Sequence, Tuple\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", + "# Sets a seed for deterministic results. Uncomment for random results each run.\n", "np.random.seed(2021)\n", "np.set_printoptions(precision=3, suppress=True, linewidth=200)" ] @@ -108,9 +109,9 @@ "property of $N-$bit strings that exhibits a **“maximal”** separation: that is, one that requires $\\Omega(N)$\n", "queries to test classically, but only O (1) quantumly.\n", "\n", - "**Fourier Checking** is a problem that provides a separation between quantum and classical computers -- $O(1)$ VS $\\tilde{\\Omega}(\\sqrt{N})$, which can be proved as optimal. Currently it only has theoretical importance - but, as it falls into the category of small quantum algorithms, it can be used to demonstrate query complexity and oracle synthesis in Cirq.\n", + "**Fourier Checking** is a problem that provides a separation between quantum and classical computers -- $O(1)$ VS $\\tilde{\\Omega}(\\sqrt{N})$, which can be proved as optimal. Currently, it only has theoretical importance - but, as it falls into the category of small quantum algorithms, it can be used to demonstrate query complexity and oracle synthesis in Cirq.\n", "\n", - "Goal of this notebook is to introduce\n", + "Goal of this notebook is to introduce:\n", "\n", "1. What is Forrelation and the Fourier Checking problem and why we are interested in it?\n", "2. What is bounded-error quantum polynomial time (BQP) and why does the Fourier Checking problem belong to it?\n", @@ -191,7 +192,15 @@ }, "outputs": [], "source": [ - "def gen_balanced_function(N):\n", + "def gen_balanced_function(N: int) -> np.ndarray:\n", + " \"\"\"Generates a balanced function for N bits.\n", + "\n", + " Creates a function 𝑓:{0,1}^N → {−1,1}\n", + " where f(x)=-1 for half of the inputs and f(x)=1 for the other half.\n", + "\n", + " Returns:\n", + " the function as represented by a 1-d numpy array of size N\n", + " \"\"\"\n", " half_size = N // 2\n", " f = np.ones(N)\n", " flip_loc = np.random.permutation(N)[:half_size]\n", @@ -199,26 +208,58 @@ " return f\n", "\n", "\n", - "def gen_constant_function(N):\n", + "def gen_constant_function(N: int) -> np.ndarray:\n", + " \"\"\"Generates a constant function for N bits.\n", + "\n", + " Creates a function 𝑓:{0,1}^𝑛 → {−1,1}\n", + " where f(x)=c for all inputs.\n", + " \n", + " c is randomly chosen as either -1 or 1, but, once chosen,\n", + " is constant for all values of x.\n", + "\n", + " Returns:\n", + " the function as represented by a 1-d numpy array of size N\n", + " \"\"\"\n", + "\n", " flip = np.random.random() > 0.5\n", " f = np.ones(N) if flip else -1 * np.ones(N)\n", - " return f" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "jOrvc5MiWWHJ" - }, - "outputs": [], - "source": [ - "def randomized_alg(f, sample_size):\n", + " return f\n", + "\n", + "def choose_random_function() -> Tuple[str, np.ndarray]:\n", + " \"\"\"Randomly choose a function from constant or balanced distributions.\n", + "\n", + " Returns:\n", + " a Tuple of the distribution (\"B\" or \"C\") and the function as an array.\n", + " \"\"\"\n", + " if np.random.rand() > 0.5:\n", + " f = gen_balanced_function(N)\n", + " dist = \"B\"\n", + " else:\n", + " f = gen_constant_function(N)\n", + " dist = \"C\"\n", + " return dist, f\n", + "\n", + "def randomized_alg(f:np.ndarray, sample_size:int) -> str:\n", + " \"\"\"Samples the function f from `sample_size` different inputs.\n", + "\n", + " Queries the function f a number of times equal to sample_size.\n", + " If all the inputs are the same, then guess that the function\n", + " is constant. If any inputs are different, then guess the function\n", + " is balanced.\n", + "\n", + " Args:\n", + " f: the function to sample\n", + " sample_size: number of times to sample the function f\n", + "\n", + " Returns:\n", + " a string representing the type of function, either\n", + " \"balanced\" or \"constant\"\n", + " \"\"\"\n", " N = len(f)\n", " sample_index = np.random.choice(N, size=sample_size)\n", " if len(set(f[sample_index])) == 2:\n", - " return \"accept\"\n", - " return \"reject\"" + " return \"balanced\"\n", + " return \"constant\"" ] }, { @@ -229,19 +270,15 @@ }, "outputs": [], "source": [ - "N = 128\n", - "K = 3\n", - "samples = 1000\n", + "N = 128 # size of the problem, n=7, N=2^7=128\n", + "samples_size_per_function = 3\n", + "number_of_functions_to_try = 1000\n", + "\n", "\n", "res = pd.DataFrame()\n", - "for _ in range(samples):\n", - " if np.random.rand() > 0.5:\n", - " f = gen_balanced_function(N)\n", - " dist = \"B\"\n", - " else:\n", - " f = gen_constant_function(N)\n", - " dist = \"C\"\n", - " decision = randomized_alg(f, K)\n", + "for _ in range(number_of_functions_to_try):\n", + " dist, f = choose_random_function()\n", + " decision = randomized_alg(f, samples_size_per_function)\n", " res = res.append({\n", " \"Distribution\": dist,\n", " \"Decision\": decision,\n", @@ -273,23 +310,18 @@ "outputs": [], "source": [ "N = 128\n", - "K = 3\n", - "repetitions = 3\n", - "samples = 1000\n", + "samples_size_per_function = 3\n", + "repetitions_of_randomized_alg = 3\n", + "number_of_functions_to_try = 1000\n", "\n", "res = pd.DataFrame()\n", - "for _ in range(samples):\n", - " if np.random.rand() > 0.5:\n", - " f = gen_balanced_function(N)\n", - " dist = \"B\"\n", - " else:\n", - " f = gen_constant_function(N)\n", - " dist = \"C\"\n", - " accept_minus_reject_count = 0\n", - " for _ in range(repetitions):\n", - " decision = randomized_alg(f, K)\n", - " accept_minus_reject_count += 1 if decision == \"accept\" else -1\n", - " final_decision = \"accept\" if accept_minus_reject_count > 0 else \"reject\"\n", + "for _ in range(number_of_functions_to_try):\n", + " dist, f = choose_random_function()\n", + " constant_minus_blanaced_count = 0\n", + " for _ in range(repetitions_of_randomized_alg):\n", + " decision = randomized_alg(f, samples_size_per_function)\n", + " constant_minus_blanaced_count += 1 if decision == \"constant\" else -1\n", + " final_decision = \"constant\" if constant_minus_blanaced_count > 0 else \"balanced\"\n", " res = res.append(\n", " {\n", " \"Distribution\": dist,\n", @@ -312,7 +344,7 @@ "source": [ "If you try the algorithm with more repetitions, you will observe the error rate decreases rapidly. You can also try different values of $K, N, $ and/or repetitions to see how the confusion matrix changes according.\n", "\n", - "After you understand the concept of BPP, it is easy to understand [bounded-error quantum polynomial time (BQP)](https://en.wikipedia.org/wiki/BQP) now. BQP is the class of decision problems solvable by a quantum computer in polynomial time, with an error probability of at most 1/3 for all instances. It is the quantum analogue to the complexity class BPP. Actually, under quantum computer, previous problem can be solved through [Deutsch–Jozsa algorithm](https://en.wikipedia.org/wiki/Deutsch%E2%80%93Jozsa_algorithm) with a single query while guaranteed to be correct always. The Fourier Checking problem that will be introduced later is belong to BQP as well." + "After you understand the concept of BPP, it is easy to understand [bounded-error quantum polynomial time (BQP)](https://en.wikipedia.org/wiki/BQP) now. BQP is the class of decision problems solvable by a quantum computer in polynomial time, with an error probability of at most 1/3 for all instances. It is the quantum analogue to the complexity class BPP. Actually, with a quantum computer, the previous problem can be solved using the[Deutsch–Jozsa algorithm](https://en.wikipedia.org/wiki/Deutsch%E2%80%93Jozsa_algorithm). This algorithm utilizes a single query and is guaranteed to be correct always. The Fourier Checking problem that will be introduced later belongs to BQP as well." ] }, { @@ -342,17 +374,15 @@ }, "outputs": [], "source": [ - "def bitwise_dot(x, y):\n", + "def bitwise_dot(x: int, y: int) -> int:\n", " \"\"\"Compute the dot product of two integers bitwise.\"\"\"\n", + " i = x & y\n", "\n", - " def bit_parity(i):\n", - " n = bin(i).count(\"1\")\n", - " return int(n % 2)\n", - "\n", - " return bit_parity(x & y)\n", + " n = bin(i).count(\"1\")\n", + " return int(n % 2)\n", "\n", "\n", - "def fourier_transform_over_z2(v):\n", + "def fourier_transform_over_z2(v: np.ndarray) -> np.ndarray:\n", " \"\"\"Fourier transform function over z_2^n group.\n", "\n", " Args:\n", @@ -361,8 +391,8 @@ " Returns:\n", " vs: a numpy array with same length as input.\n", " \"\"\"\n", - " assert len(v) & (len(v) - 1) == 0 # make sure v is 2**n long vector\n", " N = len(v)\n", + " assert bin(N).count(\"1\") == 1, \"v must be a 2**n long vector\"\n", " v_hat = np.array([0.0] * N)\n", " for y in range(N):\n", " for x in range(N):\n", @@ -376,7 +406,7 @@ "id": "fqXHBoEm7ebP" }, "source": [ - "Let's have some examples in $\\mathbb{Z}^2_2$. You should verify that both functions have same energy 4." + "Let's have some examples in $\\mathbb{Z}^2_2$. You should verify that both functions have same energy 4(as defined by Parseval's identity above).\n" ] }, { @@ -420,12 +450,15 @@ "\\end{align}\n", "where the second equality is due to the Parseval's identity.\n", "Since in this tutorial we are interesed in Boolean function, we replace the arbitary vector $u$ and $v$ by the output of Boolean function $f$ and $g$. Now we can further simplify the above definition:\n", + "\n", + "$$\n", "\\begin{align}\n", " {\\rm forrelation}(f, g) =& \\frac{\\langle f, \\widehat{g} \\rangle }{\\|f\\| \\|g\\|}\\\\\n", " =& \\frac{1}{N} \\langle f, \\widehat{g}\\rangle \\\\\n", " =& \\frac{1}{N} \\sum_{x \\in \\{0,1\\}^n}f(x)\\widehat{g}(x)\\\\\n", " =& \\frac{1}{N^{3/2}} \\sum_{x, y \\in \\{0,1\\}^n}f(x)(-1)^{x \\cdot y}g(y)\n", - "\\end{align}\n" + "\\end{align}\n", + "$$" ] }, { @@ -436,11 +469,13 @@ }, "outputs": [], "source": [ - "def get_correlation(f, g):\n", + "def get_correlation(f: np.ndarray, g: np.ndarray) -> np.ndarray:\n", + " \"\"\"Returns the classical correlation between two 1-d numpy arrays.\"\"\"\n", " return f.dot(g) / np.linalg.norm(f) / np.linalg.norm(g)\n", "\n", "\n", - "def get_forrelation(f, g):\n", + "def get_forrelation(f: np.ndarray, g: np.ndarray) -> np.ndarray:\n", + " \"\"\"Returns the forrelation over Z^2 between two 1-d numpy arrays.\"\"\"\n", " g_hat = fourier_transform_over_z2(g)\n", " return f.dot(g_hat) / np.linalg.norm(f) / np.linalg.norm(g)" ] @@ -516,18 +551,6 @@ "is either at least 0.05 or at most 0.01." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "aZie29qeGBV_" - }, - "outputs": [], - "source": [ - "n = 6\n", - "N = 2 ** n" - ] - }, { "cell_type": "code", "execution_count": null, @@ -536,8 +559,14 @@ }, "outputs": [], "source": [ - "# We can find a forrelated pair \"as promised\" through while-loop\n", - "def draw_two_distribution_from_f_set(N):\n", + "def draw_two_distribution_from_f_set(N:int)-> Tuple[np.ndarray, np.ndarray, float, float]:\n", + " \"\"\"Samples two distributions from the 'F' set above.\n", + "\n", + " Uses a while loop to guarantee a forrelated pair \"as promised\".\n", + "\n", + " Returns:\n", + " A tuple that contains the two distributions, and the correlation/forrelation.\n", + " \"\"\"\n", " sgn = lambda x: 1 if x >= 0 else -1\n", " forrelation = 0.2\n", " while (abs(forrelation)**2 < 0.05) and (abs(forrelation)**2 > 0.01):\n", @@ -550,7 +579,14 @@ " return fs, gs, forrelation, correlation\n", "\n", "\n", - "def draw_two_distribution_from_u_set(N):\n", + "def draw_two_distribution_from_u_set(N:int)-> Tuple[np.ndarray, np.ndarray, float, float]:\n", + " \"\"\"Samples two distributions from the 'U' set above.\n", + "\n", + " Uses a while loop to guarantee a forrelated pair \"as promised\".\n", + " \n", + " Returns:\n", + " A tuple that contains the two distributions, and the correlation/forrelation.\n", + " \"\"\"\n", " sgn = lambda x: 1 if x >= 0 else -1\n", " forrelation = 0.2\n", " while (abs(forrelation)**2 < 0.05) and (abs(forrelation)**2 > 0.01):\n", @@ -571,31 +607,30 @@ }, "outputs": [], "source": [ - "fs, gs, forrelation, correlation = draw_two_distribution_from_u_set(N)\n", + "n = 6\n", + "N = 2 ** n\n", + "\n", + "fs, gs, forrelation, correlation = draw_two_distribution_from_f_set(N)\n", + "print('Correlation and forrelation from F set')\n", "print(f\"fs: {list(fs)}\")\n", "print(f\"gs: {list(gs)}\")\n", - "\n", + "print(f'Correlation: {correlation} Forrelation: {forrelation}')\n", "plt.figure(figsize=(15, 5))\n", "plt.stem(fs, use_line_collection=True)\n", "plt.stem(gs, linefmt='--r', markerfmt='ro', use_line_collection=True)\n", - "plt.title(f\"Correlation: {correlation} Forrelation: {forrelation}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "xIotvjzjOvxA" - }, - "outputs": [], - "source": [ - "fs, gs, forrelation, correlation = draw_two_distribution_from_f_set(N)\n", + "plt.title(f\"Two distributions from F set\")\n", + "\n", + "print('')\n", + "print('Correlation and forrelation from U set')\n", + "fs, gs, forrelation, correlation = draw_two_distribution_from_u_set(N)\n", "print(f\"fs: {list(fs)}\")\n", "print(f\"gs: {list(gs)}\")\n", + "print(f'Correlation: {correlation} Forrelation: {forrelation}')\n", + "\n", "plt.figure(figsize=(15, 5))\n", "plt.stem(fs, use_line_collection=True)\n", - "plt.stem(gs, linefmt='--r', markerfmt=\"ro\", use_line_collection=True)\n", - "plt.title(f\"Correlation: {correlation} Forrelation: {forrelation}\")" + "plt.stem(gs, linefmt='--r', markerfmt='ro', use_line_collection=True)\n", + "_ = plt.title(f\"Two distributions from U set\")\n" ] }, { @@ -655,10 +690,13 @@ "\n", "It is shown in the [paper](https://arxiv.org/pdf/0910.4698.pdf) that\n", "\n", + "$$\n", "\\begin{align}\n", " {\\rm Pr}_{\\langle f, g \\rangle \\sim \\mathcal{U}} [p(f,g) \\geq 0.01] \\leq& \\; \\frac{100}{N} \\\\\n", " {\\rm Pr}_{\\langle f, g \\rangle \\sim \\mathcal{F}} [p(f,g) \\geq 0.05] \\geq&\\;\\frac{1}{50} \\\\\n", "\\end{align}\n", + "$$\n", + "\n", "This implies that the probability of the forrelation square between two functions drawing from uniform distribution $\\mathcal{U}$ having larger than 0.01 will decaying quickly when we have more number of qubits. Hence, the Promise Fourier Checking problem can be solved through simply accepting when $p(f,g) \\geq 0.05$ and rejecting when $p(f,g) \\leq 0.01$ with constant error probability, using $O(1)$ queries to $f$ and $g$." ] }, @@ -672,6 +710,7 @@ "\n", "Above algorithm is simple and straightforward to implement. It only involves with Hadmard gates and function oracles. Implementation of oracles based on truth table in Cirq is just simple diagnal gates. To see that, let's use a oracle defined over $\\mathbb{Z}_2^2$ as example:\n", "\n", + "$$\n", "\\begin{align}\n", " \\sum_{x\\in \\{0, 1\\}^2} f(x)|x\\rangle\n", " =&\\frac{1}{2}\\Big(f(0,0)|00\\rangle + f(0,1)|01\\rangle+f(1,0)|10\\rangle + f(1,1) |11\\rangle\\Big)\\\\\n", @@ -688,6 +727,7 @@ " 1/2\n", " \\end{array} \\right]\n", "\\end{align}\n", + "$$\n", "\n", "It is crucial to note that the output of $f$ is either 1 or -1, so the diagonal matrix is unitary." ] @@ -700,7 +740,12 @@ }, "outputs": [], "source": [ - "def oracle(fs, qubits):\n", + "def oracle(fs: np.ndarray, qubits: Sequence[cirq.Qid]) -> cirq.Operation:\n", + " \"\"\"Construct a sample oracle using a function as above.\n", + "\n", + " This will create an operation with a unitary matrix that is diagonal\n", + " and whose entries correspond to the values of the input function 'fs'.\n", + " \"\"\"\n", " return cirq.MatrixGate(np.diag(fs).astype(complex))(*qubits)\n", "\n", "\n", @@ -768,7 +813,7 @@ "id": "m5Qzdo8rTPxJ" }, "source": [ - "In reality, we can measure the state of qubits only. Each measurement will only produce one state. In order to eastimate the probability, we can do 100 repeitations and use frequency of showing 0 state as the approximation of the probability of state 0." + "In reality, we can measure the state of qubits only. Each measurement will only produce one state. In order to estimate the probability, we can do 100 repetitions and use the frequency of the 0 state as the approximation of its probability." ] }, { @@ -878,11 +923,7 @@ ], "metadata": { "colab": { - "collapsed_sections": [ - "HNCLXihE01A7", - "spyqzGyf5kGW", - "02GAaXsAsYOW" - ], + "collapsed_sections": [], "name": "fourier_checking.ipynb", "toc_visible": true }, From 1eda55fd3ed7e89a339ee70bf5a93ba03d8b3215 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Sun, 10 Jul 2022 19:18:43 -0700 Subject: [PATCH 3/3] Fix testing regexes --- docs/experiments/fourier_checking.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/experiments/fourier_checking.tst b/docs/experiments/fourier_checking.tst index 50108aef936..b22895f1254 100644 --- a/docs/experiments/fourier_checking.tst +++ b/docs/experiments/fourier_checking.tst @@ -16,4 +16,4 @@ num_rounds = 1000->num_rounds = 2 N = 128->N = 4 -samples = 1000->samples = 2 +number_of_functions_to_try = 1000->number_of_functions_to_try = 2